diff options
1081 files changed, 25977 insertions, 14147 deletions
diff --git a/.clang-tidy b/.clang-tidy index 1cc0e6e7f4a..42ce52d58ca 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,6 +1,8 @@ # The warnings below are disabled because they are too pedantic and not worth fixing. # Some of them will be enabled as part of the Clang-Tidy task, see T78535. +# NOTE: No comments in the list below is allowed. Clang-tidy will ignore items after comments in the lists flag list. +# This is because the comment is not a valid list item and it will stop parsing flags if a list item is a comment. Checks: > -*, readability-*, @@ -14,10 +16,9 @@ Checks: > -readability-make-member-function-const, -readability-suspicious-call-argument, -readability-redundant-member-init, - -readability-misleading-indentation, - -readability-use-anyofallof, + -readability-identifier-length, -readability-function-cognitive-complexity, @@ -35,6 +36,8 @@ Checks: > -bugprone-redundant-branch-condition, + -bugprone-suspicious-include, + modernize-*, -modernize-use-auto, -modernize-use-trailing-return-type, @@ -42,8 +45,6 @@ Checks: > -modernize-use-nodiscard, -modernize-loop-convert, -modernize-pass-by-value, - # Cannot be enabled yet, because using raw string literals in tests breaks - # the windows compiler currently. -modernize-raw-string-literal, -modernize-return-braced-init-list diff --git a/CMakeLists.txt b/CMakeLists.txt index f5660b3653b..13f4e6b9cc4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -781,7 +781,9 @@ set_and_warn_dependency(WITH_BOOST WITH_OPENCOLORIO OFF) set_and_warn_dependency(WITH_BOOST WITH_QUADRIFLOW OFF) set_and_warn_dependency(WITH_BOOST WITH_USD OFF) set_and_warn_dependency(WITH_BOOST WITH_ALEMBIC OFF) -set_and_warn_dependency(WITH_PUGIXML WITH_CYCLES_OSL OFF) +if(WITH_CYCLES) + set_and_warn_dependency(WITH_PUGIXML WITH_CYCLES_OSL OFF) +endif() set_and_warn_dependency(WITH_PUGIXML WITH_OPENIMAGEIO OFF) if(WITH_BOOST AND NOT (WITH_CYCLES OR WITH_OPENIMAGEIO OR WITH_INTERNATIONAL OR @@ -1456,14 +1458,6 @@ if(WITH_LIBMV OR WITH_GTESTS OR (WITH_CYCLES AND WITH_CYCLES_LOGGING)) endif() #----------------------------------------------------------------------------- -# Configure Ceres - -if(WITH_LIBMV) - # We always have C++11 which includes unordered_map. - set(CERES_DEFINES "-DCERES_STD_UNORDERED_MAP;-DCERES_USE_CXX_THREADS") -endif() - -#----------------------------------------------------------------------------- # Extra limits to number of jobs running in parallel for some kind os tasks. # Only supported by Ninja build system currently. if("${CMAKE_GENERATOR}" MATCHES "Ninja" AND WITH_NINJA_POOL_JOBS) @@ -1539,7 +1533,6 @@ endif() if(CMAKE_COMPILER_IS_GNUCC) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ALL -Wall) - ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_CAST_ALIGN -Wcast-align) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_IMPLICIT_FUNCTION_DECLARATION -Werror=implicit-function-declaration) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_RETURN_TYPE -Werror=return-type) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_VLA -Werror=vla) diff --git a/build_files/build_environment/cmake/download.cmake b/build_files/build_environment/cmake/download.cmake index e305b05ee70..81e7f7ab3fe 100644 --- a/build_files/build_environment/cmake/download.cmake +++ b/build_files/build_environment/cmake/download.cmake @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later ## Update and uncomment this in the release branch -set(BLENDER_VERSION 3.2) +# set(BLENDER_VERSION 3.1) function(download_source dep) set(TARGET_FILE ${${dep}_FILE}) diff --git a/build_files/cmake/Modules/FindUSD.cmake b/build_files/cmake/Modules/FindUSD.cmake index d8f2ee22e6e..0fd5f06bb35 100644 --- a/build_files/cmake/Modules/FindUSD.cmake +++ b/build_files/cmake/Modules/FindUSD.cmake @@ -64,6 +64,7 @@ ENDIF() MARK_AS_ADVANCED( USD_INCLUDE_DIR USD_LIBRARY_DIR + USD_LIBRARY ) UNSET(_usd_SEARCH_DIRS) diff --git a/build_files/cmake/Modules/FindWebP.cmake b/build_files/cmake/Modules/FindWebP.cmake index 741c48ec447..2d8923a7fe6 100644 --- a/build_files/cmake/Modules/FindWebP.cmake +++ b/build_files/cmake/Modules/FindWebP.cmake @@ -74,4 +74,9 @@ ENDIF() MARK_AS_ADVANCED( WEBP_INCLUDE_DIR WEBP_LIBRARY_DIR + + # Generated names. + WEBP_WEBPDEMUX_LIBRARY + WEBP_WEBPMUX_LIBRARY + WEBP_WEBP_LIBRARY ) diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index c5d2049b292..40c25abd585 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -104,7 +104,7 @@ string(APPEND CMAKE_MODULE_LINKER_FLAGS " /SAFESEH:NO /ignore:4099") list(APPEND PLATFORM_LINKLIBS ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 Comctl32 version advapi32 shfolder shell32 ole32 oleaut32 uuid psapi Dbghelp Shlwapi - pathcch Shcore + pathcch Shcore Dwmapi ) if(WITH_INPUT_IME) diff --git a/build_files/cmake/project_info.py b/build_files/cmake/project_info.py index 46fd538f675..0661db3a18c 100755 --- a/build_files/cmake/project_info.py +++ b/build_files/cmake/project_info.py @@ -170,7 +170,7 @@ def cmake_advanced_info() -> Union[Tuple[List[str], List[Tuple[str, str]]], Tupl project_path = create_eclipse_project() if not exists(project_path): - print("Generating Eclipse Prokect File Failed: %r not found" % project_path) + print("Generating Eclipse Project File Failed: %r not found" % project_path) return None, None from xml.dom.minidom import parse diff --git a/build_files/config/pipeline_config.yaml b/build_files/config/pipeline_config.yaml index d1f1fe78d21..8222f2ff0b9 100644 --- a/build_files/config/pipeline_config.yaml +++ b/build_files/config/pipeline_config.yaml @@ -5,38 +5,38 @@ update-code: git: submodules: - - branch: blender-v3.2-release + - branch: master commit_id: HEAD path: release/scripts/addons - - branch: blender-v3.2-release + - branch: master commit_id: HEAD path: release/scripts/addons_contrib - - branch: blender-v3.2-release + - branch: master commit_id: HEAD path: release/datafiles/locale - - branch: blender-v3.2-release + - branch: master commit_id: HEAD path: source/tools svn: libraries: darwin-arm64: - branch: tags/blender-3.2-release + branch: trunk commit_id: HEAD path: lib/darwin_arm64 darwin-x86_64: - branch: tags/blender-3.2-release + branch: trunk commit_id: HEAD path: lib/darwin linux-x86_64: - branch: tags/blender-3.2-release + branch: trunk commit_id: HEAD path: lib/linux_centos7_x86_64 windows-amd64: - branch: tags/blender-3.2-release + branch: trunk commit_id: HEAD path: lib/win64_vc15 tests: - branch: tags/blender-3.2-release + branch: trunk commit_id: HEAD path: lib/tests benchmarks: diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index b8f54f548b4..0176a888377 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = Blender # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = V3.2 +PROJECT_NUMBER = V3.3 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/doc/python_api/examples/gpu.6.py b/doc/python_api/examples/gpu.6.py index be164a03028..5576b2d0bfe 100644 --- a/doc/python_api/examples/gpu.6.py +++ b/doc/python_api/examples/gpu.6.py @@ -29,3 +29,36 @@ def draw(): bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL') + +""" +3D Image +-------- + +Similar to the 2D Image shader, but works with 3D positions for the image vertices. +To use this example you have to provide an image that should be displayed. +""" +import bpy +import gpu +from gpu_extras.batch import batch_for_shader + +IMAGE_NAME = "Untitled" +image = bpy.data.images[IMAGE_NAME] +texture = gpu.texture.from_image(image) + +shader = gpu.shader.from_builtin('3D_IMAGE') +batch = batch_for_shader( + shader, 'TRIS', + { + "pos": ((0, 0, 0), (0, 1, 1), (1, 1, 1), (1, 1, 1), (1, 0, 0), (0, 0, 0)), + "texCoord": ((0, 0), (0, 1), (1, 1), (1, 1), (1, 0), (0, 0)), + }, +) + + +def draw(): + shader.bind() + shader.uniform_sampler("image", texture) + batch.draw(shader) + + +bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW') diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index d87d8b71ce5..0a20bd83c22 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1200,34 +1200,17 @@ def pycontext2sphinx(basepath): del write_contex_cls # end - # nasty, get strings directly from Blender because there is no other way to get it - import ctypes - - context_strings = ( - "screen_context_dir", - "view3d_context_dir", - "buttons_context_dir", - "image_context_dir", - "node_context_dir", - "text_context_dir", - "clip_context_dir", - "sequencer_context_dir", - "file_context_dir", - ) + # Internal API call only intended to be used to extract context members. + from _bpy import context_members + context_member_map = context_members() + del context_members # Track unique for `context_strings` to validate `context_type_map`. unique_context_strings = set() - blend_cdll = ctypes.CDLL("") - for ctx_str in context_strings: + for ctx_str, ctx_members in sorted(context_member_map.items()): subsection = "%s Context" % ctx_str.split("_")[0].title() fw("\n%s\n%s\n\n" % (subsection, (len(subsection) * '-'))) - - attr = ctypes.addressof(getattr(blend_cdll, ctx_str)) - c_char_p_p = ctypes.POINTER(ctypes.c_char_p) - char_array = c_char_p_p.from_address(attr) - i = 0 - while char_array[i] is not None: - member = ctypes.string_at(char_array[i]).decode(encoding="ascii") + for member in ctx_members: unique_all_len = len(unique) unique.add(member) member_visited = unique_all_len == len(unique) @@ -1247,7 +1230,6 @@ def pycontext2sphinx(basepath): "Error: context key %r not found in context_type_map; update %s" % (member, __file__)) from None fw(" :type: %s :class:`bpy.types.%s`\n\n" % ("sequence of " if is_seq else "", member_type)) - i += 1 # generate typemap... # for member in sorted(unique_context_strings): @@ -2173,10 +2155,7 @@ def rna2sphinx(basepath): # context if "bpy.context" not in EXCLUDE_MODULES: - # one of a kind, context doc (uses ctypes to extract info!) - # doesn't work on mac and windows - if PLATFORM not in {"darwin", "windows"}: - pycontext2sphinx(basepath) + pycontext2sphinx(basepath) # internal modules write_rst_bpy(basepath) # bpy, disabled by default diff --git a/extern/ceres/CMakeLists.txt b/extern/ceres/CMakeLists.txt index 1d8a611b107..b723a4466fb 100644 --- a/extern/ceres/CMakeLists.txt +++ b/extern/ceres/CMakeLists.txt @@ -1,16 +1,11 @@ # SPDX-License-Identifier: GPL-2.0-or-later # Copyright 2012 Blender Foundation. All rights reserved. -# NOTE: This file is automatically generated by bundle.sh script -# If you're doing changes in this file, please update template -# in that script too - set(INC . include internal config - ../gflags/src ) set(INC_SYS @@ -20,279 +15,296 @@ set(INC_SYS ) set(SRC - internal/ceres/accelerate_sparse.cc - internal/ceres/array_utils.cc - internal/ceres/blas.cc - internal/ceres/block_evaluate_preparer.cc - internal/ceres/block_jacobian_writer.cc - internal/ceres/block_jacobi_preconditioner.cc - internal/ceres/block_random_access_dense_matrix.cc - internal/ceres/block_random_access_diagonal_matrix.cc - internal/ceres/block_random_access_matrix.cc - internal/ceres/block_random_access_sparse_matrix.cc - internal/ceres/block_sparse_matrix.cc - internal/ceres/block_structure.cc - internal/ceres/callbacks.cc - internal/ceres/canonical_views_clustering.cc - internal/ceres/c_api.cc - internal/ceres/cgnr_solver.cc - internal/ceres/compressed_col_sparse_matrix_utils.cc - internal/ceres/compressed_row_jacobian_writer.cc - internal/ceres/compressed_row_sparse_matrix.cc - internal/ceres/conditioned_cost_function.cc - internal/ceres/conjugate_gradients_solver.cc - internal/ceres/context.cc - internal/ceres/context_impl.cc - internal/ceres/coordinate_descent_minimizer.cc - internal/ceres/corrector.cc - internal/ceres/covariance.cc - internal/ceres/covariance_impl.cc - internal/ceres/cxsparse.cc - internal/ceres/dense_normal_cholesky_solver.cc - internal/ceres/dense_qr_solver.cc - internal/ceres/dense_sparse_matrix.cc - internal/ceres/detect_structure.cc - internal/ceres/dogleg_strategy.cc - internal/ceres/dynamic_compressed_row_jacobian_writer.cc - internal/ceres/dynamic_compressed_row_sparse_matrix.cc - internal/ceres/dynamic_sparse_normal_cholesky_solver.cc - internal/ceres/eigensparse.cc - internal/ceres/evaluator.cc - internal/ceres/file.cc - internal/ceres/float_cxsparse.cc - internal/ceres/float_suitesparse.cc - internal/ceres/function_sample.cc - internal/ceres/generated/partitioned_matrix_view_d_d_d.cc - internal/ceres/generated/schur_eliminator_d_d_d.cc - internal/ceres/gradient_checker.cc - internal/ceres/gradient_checking_cost_function.cc - internal/ceres/gradient_problem.cc - internal/ceres/gradient_problem_solver.cc - internal/ceres/implicit_schur_complement.cc - internal/ceres/inner_product_computer.cc - internal/ceres/is_close.cc - internal/ceres/iterative_refiner.cc - internal/ceres/iterative_schur_complement_solver.cc - internal/ceres/lapack.cc - internal/ceres/levenberg_marquardt_strategy.cc - internal/ceres/linear_least_squares_problems.cc - internal/ceres/linear_operator.cc - internal/ceres/linear_solver.cc - internal/ceres/line_search.cc - internal/ceres/line_search_direction.cc - internal/ceres/line_search_minimizer.cc - internal/ceres/line_search_preprocessor.cc - internal/ceres/local_parameterization.cc - internal/ceres/loss_function.cc - internal/ceres/low_rank_inverse_hessian.cc - internal/ceres/minimizer.cc - internal/ceres/normal_prior.cc - internal/ceres/parallel_for_cxx.cc - internal/ceres/parallel_for_nothreads.cc - internal/ceres/parallel_for_openmp.cc - internal/ceres/parallel_utils.cc - internal/ceres/parameter_block_ordering.cc - internal/ceres/partitioned_matrix_view.cc - internal/ceres/polynomial.cc - internal/ceres/preconditioner.cc - internal/ceres/preprocessor.cc - internal/ceres/problem.cc - internal/ceres/problem_impl.cc - internal/ceres/program.cc - internal/ceres/reorder_program.cc - internal/ceres/residual_block.cc - internal/ceres/residual_block_utils.cc - internal/ceres/schur_complement_solver.cc - internal/ceres/schur_eliminator.cc - internal/ceres/schur_jacobi_preconditioner.cc - internal/ceres/schur_templates.cc - internal/ceres/scratch_evaluate_preparer.cc - internal/ceres/single_linkage_clustering.cc - internal/ceres/solver.cc - internal/ceres/solver_utils.cc - internal/ceres/sparse_cholesky.cc - internal/ceres/sparse_matrix.cc - internal/ceres/sparse_normal_cholesky_solver.cc - internal/ceres/split.cc - internal/ceres/stringprintf.cc - internal/ceres/subset_preconditioner.cc - internal/ceres/suitesparse.cc - internal/ceres/thread_pool.cc - internal/ceres/thread_token_provider.cc - internal/ceres/triplet_sparse_matrix.cc - internal/ceres/trust_region_minimizer.cc - internal/ceres/trust_region_preprocessor.cc - internal/ceres/trust_region_step_evaluator.cc - internal/ceres/trust_region_strategy.cc - internal/ceres/types.cc - internal/ceres/visibility_based_preconditioner.cc - internal/ceres/visibility.cc - internal/ceres/wall_time.cc + include/ceres/autodiff_cost_function.h + include/ceres/autodiff_first_order_function.h + include/ceres/autodiff_local_parameterization.h + include/ceres/autodiff_manifold.h + include/ceres/c_api.h + include/ceres/ceres.h + include/ceres/conditioned_cost_function.h + include/ceres/context.h + include/ceres/cost_function.h + include/ceres/cost_function_to_functor.h + include/ceres/covariance.h + include/ceres/crs_matrix.h + include/ceres/cubic_interpolation.h + include/ceres/dynamic_autodiff_cost_function.h + include/ceres/dynamic_cost_function.h + include/ceres/dynamic_cost_function_to_functor.h + include/ceres/dynamic_numeric_diff_cost_function.h + include/ceres/evaluation_callback.h + include/ceres/first_order_function.h + include/ceres/gradient_checker.h + include/ceres/gradient_problem.h + include/ceres/gradient_problem_solver.h + include/ceres/iteration_callback.h + include/ceres/jet.h + include/ceres/jet_fwd.h + include/ceres/line_manifold.h + include/ceres/local_parameterization.h + include/ceres/loss_function.h + include/ceres/manifold.h + include/ceres/manifold_test_utils.h + include/ceres/normal_prior.h + include/ceres/numeric_diff_cost_function.h + include/ceres/numeric_diff_first_order_function.h + include/ceres/numeric_diff_options.h + include/ceres/ordered_groups.h + include/ceres/problem.h + include/ceres/product_manifold.h + include/ceres/rotation.h + include/ceres/sized_cost_function.h + include/ceres/solver.h + include/ceres/sphere_manifold.h + include/ceres/tiny_solver.h + include/ceres/tiny_solver_autodiff_function.h + include/ceres/tiny_solver_cost_function_adapter.h + include/ceres/types.h + include/ceres/version.h + + include/ceres/internal/array_selector.h + include/ceres/internal/autodiff.h + include/ceres/internal/disable_warnings.h + include/ceres/internal/eigen.h + include/ceres/internal/fixed_array.h + include/ceres/internal/householder_vector.h + include/ceres/internal/integer_sequence_algorithm.h + include/ceres/internal/jet_traits.h + include/ceres/internal/line_parameterization.h + include/ceres/internal/memory.h + include/ceres/internal/numeric_diff.h + include/ceres/internal/parameter_dims.h + include/ceres/internal/port.h + include/ceres/internal/reenable_warnings.h + include/ceres/internal/sphere_manifold_functions.h + include/ceres/internal/variadic_evaluate.h + + internal/ceres/accelerate_sparse.cc + internal/ceres/accelerate_sparse.h + internal/ceres/array_utils.cc + internal/ceres/array_utils.h + internal/ceres/block_evaluate_preparer.cc + internal/ceres/block_evaluate_preparer.h + internal/ceres/block_jacobi_preconditioner.cc + internal/ceres/block_jacobi_preconditioner.h + internal/ceres/block_jacobian_writer.cc + internal/ceres/block_jacobian_writer.h + internal/ceres/block_random_access_dense_matrix.cc + internal/ceres/block_random_access_dense_matrix.h + internal/ceres/block_random_access_diagonal_matrix.cc + internal/ceres/block_random_access_diagonal_matrix.h + internal/ceres/block_random_access_matrix.cc + internal/ceres/block_random_access_matrix.h + internal/ceres/block_random_access_sparse_matrix.cc + internal/ceres/block_random_access_sparse_matrix.h + internal/ceres/block_sparse_matrix.cc + internal/ceres/block_sparse_matrix.h + internal/ceres/block_structure.cc + internal/ceres/block_structure.h + internal/ceres/c_api.cc + internal/ceres/callbacks.cc + internal/ceres/callbacks.h + internal/ceres/canonical_views_clustering.cc + internal/ceres/canonical_views_clustering.h + internal/ceres/casts.h + internal/ceres/cgnr_linear_operator.h + internal/ceres/cgnr_solver.cc + internal/ceres/cgnr_solver.h + internal/ceres/compressed_col_sparse_matrix_utils.cc + internal/ceres/compressed_col_sparse_matrix_utils.h + internal/ceres/compressed_row_jacobian_writer.cc + internal/ceres/compressed_row_jacobian_writer.h + internal/ceres/compressed_row_sparse_matrix.cc + internal/ceres/compressed_row_sparse_matrix.h + internal/ceres/concurrent_queue.h + internal/ceres/conditioned_cost_function.cc + internal/ceres/conjugate_gradients_solver.cc + internal/ceres/conjugate_gradients_solver.h + internal/ceres/context.cc + internal/ceres/context_impl.cc + internal/ceres/context_impl.h + internal/ceres/coordinate_descent_minimizer.cc + internal/ceres/coordinate_descent_minimizer.h + internal/ceres/corrector.cc + internal/ceres/corrector.h + internal/ceres/cost_function.cc + internal/ceres/covariance.cc + internal/ceres/covariance_impl.cc + internal/ceres/covariance_impl.h + internal/ceres/cuda_buffer.h + internal/ceres/cxsparse.cc + internal/ceres/cxsparse.h + internal/ceres/dense_cholesky.cc + internal/ceres/dense_cholesky.h + internal/ceres/dense_jacobian_writer.h + internal/ceres/dense_normal_cholesky_solver.cc + internal/ceres/dense_normal_cholesky_solver.h + internal/ceres/dense_qr.cc + internal/ceres/dense_qr.h + internal/ceres/dense_qr_solver.cc + internal/ceres/dense_qr_solver.h + internal/ceres/dense_sparse_matrix.cc + internal/ceres/dense_sparse_matrix.h + internal/ceres/detect_structure.cc + internal/ceres/detect_structure.h + internal/ceres/dogleg_strategy.cc + internal/ceres/dogleg_strategy.h + internal/ceres/dynamic_compressed_row_finalizer.h + internal/ceres/dynamic_compressed_row_jacobian_writer.cc + internal/ceres/dynamic_compressed_row_jacobian_writer.h + internal/ceres/dynamic_compressed_row_sparse_matrix.cc + internal/ceres/dynamic_compressed_row_sparse_matrix.h + internal/ceres/dynamic_sparse_normal_cholesky_solver.cc + internal/ceres/dynamic_sparse_normal_cholesky_solver.h + internal/ceres/eigensparse.cc + internal/ceres/eigensparse.h + internal/ceres/evaluation_callback.cc + internal/ceres/evaluator.cc + internal/ceres/evaluator.h + internal/ceres/execution_summary.h + internal/ceres/file.cc + internal/ceres/file.h + internal/ceres/first_order_function.cc + internal/ceres/float_cxsparse.cc + internal/ceres/float_cxsparse.h + internal/ceres/float_suitesparse.cc + internal/ceres/float_suitesparse.h + internal/ceres/function_sample.cc + internal/ceres/function_sample.h + internal/ceres/gradient_checker.cc + internal/ceres/gradient_checking_cost_function.cc + internal/ceres/gradient_checking_cost_function.h + internal/ceres/gradient_problem.cc + internal/ceres/gradient_problem_evaluator.h + internal/ceres/gradient_problem_solver.cc + internal/ceres/graph.h + internal/ceres/graph_algorithms.h + internal/ceres/implicit_schur_complement.cc + internal/ceres/implicit_schur_complement.h + internal/ceres/inner_product_computer.cc + internal/ceres/inner_product_computer.h + internal/ceres/invert_psd_matrix.h + internal/ceres/is_close.cc + internal/ceres/is_close.h + internal/ceres/iteration_callback.cc + internal/ceres/iterative_refiner.cc + internal/ceres/iterative_refiner.h + internal/ceres/iterative_schur_complement_solver.cc + internal/ceres/iterative_schur_complement_solver.h + internal/ceres/levenberg_marquardt_strategy.cc + internal/ceres/levenberg_marquardt_strategy.h + internal/ceres/line_search.cc + internal/ceres/line_search.h + internal/ceres/line_search_direction.cc + internal/ceres/line_search_direction.h + internal/ceres/line_search_minimizer.cc + internal/ceres/line_search_minimizer.h + internal/ceres/line_search_preprocessor.cc + internal/ceres/line_search_preprocessor.h + internal/ceres/linear_least_squares_problems.cc + internal/ceres/linear_least_squares_problems.h + internal/ceres/linear_operator.cc + internal/ceres/linear_operator.h + internal/ceres/linear_solver.cc + internal/ceres/linear_solver.h + internal/ceres/local_parameterization.cc + internal/ceres/loss_function.cc + internal/ceres/low_rank_inverse_hessian.cc + internal/ceres/low_rank_inverse_hessian.h + internal/ceres/manifold.cc + internal/ceres/manifold_adapter.h + internal/ceres/map_util.h + internal/ceres/minimizer.cc + internal/ceres/minimizer.h + internal/ceres/normal_prior.cc + internal/ceres/pair_hash.h + internal/ceres/parallel_for.h + internal/ceres/parallel_for_cxx.cc + internal/ceres/parallel_for_nothreads.cc + internal/ceres/parallel_for_openmp.cc + internal/ceres/parallel_utils.cc + internal/ceres/parallel_utils.h + internal/ceres/parameter_block.h + internal/ceres/parameter_block_ordering.cc + internal/ceres/parameter_block_ordering.h + internal/ceres/partitioned_matrix_view.cc + internal/ceres/partitioned_matrix_view.h + internal/ceres/partitioned_matrix_view_impl.h + internal/ceres/polynomial.cc + internal/ceres/polynomial.h + internal/ceres/preconditioner.cc + internal/ceres/preconditioner.h + internal/ceres/preprocessor.cc + internal/ceres/preprocessor.h + internal/ceres/problem.cc + internal/ceres/problem_impl.cc + internal/ceres/problem_impl.h + internal/ceres/program.cc + internal/ceres/program.h + internal/ceres/program_evaluator.h + internal/ceres/random.h + internal/ceres/reorder_program.cc + internal/ceres/reorder_program.h + internal/ceres/residual_block.cc + internal/ceres/residual_block.h + internal/ceres/residual_block_utils.cc + internal/ceres/residual_block_utils.h + internal/ceres/schur_complement_solver.cc + internal/ceres/schur_complement_solver.h + internal/ceres/schur_eliminator.cc + internal/ceres/schur_eliminator.h + internal/ceres/schur_eliminator_impl.h + internal/ceres/schur_jacobi_preconditioner.cc + internal/ceres/schur_jacobi_preconditioner.h + internal/ceres/schur_templates.cc + internal/ceres/schur_templates.h + internal/ceres/scoped_thread_token.h + internal/ceres/scratch_evaluate_preparer.cc + internal/ceres/scratch_evaluate_preparer.h + internal/ceres/single_linkage_clustering.cc + internal/ceres/single_linkage_clustering.h + internal/ceres/small_blas.h + internal/ceres/small_blas_generic.h + internal/ceres/solver.cc + internal/ceres/solver_utils.cc + internal/ceres/solver_utils.h + internal/ceres/sparse_cholesky.cc + internal/ceres/sparse_cholesky.h + internal/ceres/sparse_matrix.cc + internal/ceres/sparse_matrix.h + internal/ceres/sparse_normal_cholesky_solver.cc + internal/ceres/sparse_normal_cholesky_solver.h + internal/ceres/stl_util.h + internal/ceres/stringprintf.cc + internal/ceres/stringprintf.h + internal/ceres/subset_preconditioner.cc + internal/ceres/subset_preconditioner.h + internal/ceres/suitesparse.cc + internal/ceres/suitesparse.h + internal/ceres/thread_pool.cc + internal/ceres/thread_pool.h + internal/ceres/thread_token_provider.cc + internal/ceres/thread_token_provider.h + internal/ceres/triplet_sparse_matrix.cc + internal/ceres/triplet_sparse_matrix.h + internal/ceres/trust_region_minimizer.cc + internal/ceres/trust_region_minimizer.h + internal/ceres/trust_region_preprocessor.cc + internal/ceres/trust_region_preprocessor.h + internal/ceres/trust_region_step_evaluator.cc + internal/ceres/trust_region_step_evaluator.h + internal/ceres/trust_region_strategy.cc + internal/ceres/trust_region_strategy.h + internal/ceres/types.cc + internal/ceres/visibility.cc + internal/ceres/visibility.h + internal/ceres/visibility_based_preconditioner.cc + internal/ceres/visibility_based_preconditioner.h + internal/ceres/wall_time.cc + internal/ceres/wall_time.h - include/ceres/autodiff_cost_function.h - include/ceres/autodiff_first_order_function.h - include/ceres/autodiff_local_parameterization.h - include/ceres/c_api.h - include/ceres/ceres.h - include/ceres/conditioned_cost_function.h - include/ceres/context.h - include/ceres/cost_function.h - include/ceres/cost_function_to_functor.h - include/ceres/covariance.h - include/ceres/crs_matrix.h - include/ceres/cubic_interpolation.h - include/ceres/dynamic_autodiff_cost_function.h - include/ceres/dynamic_cost_function.h - include/ceres/dynamic_cost_function_to_functor.h - include/ceres/dynamic_numeric_diff_cost_function.h - include/ceres/evaluation_callback.h - include/ceres/first_order_function.h - include/ceres/gradient_checker.h - include/ceres/gradient_problem.h - include/ceres/gradient_problem_solver.h - include/ceres/internal/array_selector.h - include/ceres/internal/autodiff.h - include/ceres/internal/disable_warnings.h - include/ceres/internal/eigen.h - include/ceres/internal/fixed_array.h - include/ceres/internal/householder_vector.h - include/ceres/internal/integer_sequence_algorithm.h - include/ceres/internal/line_parameterization.h - include/ceres/internal/memory.h - include/ceres/internal/numeric_diff.h - include/ceres/internal/parameter_dims.h - include/ceres/internal/port.h - include/ceres/internal/reenable_warnings.h - include/ceres/internal/variadic_evaluate.h - include/ceres/iteration_callback.h - include/ceres/jet.h - include/ceres/local_parameterization.h - include/ceres/loss_function.h - include/ceres/normal_prior.h - include/ceres/numeric_diff_cost_function.h - include/ceres/numeric_diff_options.h - include/ceres/ordered_groups.h - include/ceres/problem.h - include/ceres/rotation.h - include/ceres/sized_cost_function.h - include/ceres/solver.h - include/ceres/tiny_solver_autodiff_function.h - include/ceres/tiny_solver_cost_function_adapter.h - include/ceres/tiny_solver.h - include/ceres/types.h - include/ceres/version.h - internal/ceres/accelerate_sparse.h - internal/ceres/array_utils.h - internal/ceres/blas.h - internal/ceres/block_evaluate_preparer.h - internal/ceres/block_jacobian_writer.h - internal/ceres/block_jacobi_preconditioner.h - internal/ceres/block_random_access_dense_matrix.h - internal/ceres/block_random_access_diagonal_matrix.h - internal/ceres/block_random_access_matrix.h - internal/ceres/block_random_access_sparse_matrix.h - internal/ceres/block_sparse_matrix.h - internal/ceres/block_structure.h - internal/ceres/callbacks.h - internal/ceres/canonical_views_clustering.h - internal/ceres/casts.h - internal/ceres/cgnr_linear_operator.h - internal/ceres/cgnr_solver.h - internal/ceres/compressed_col_sparse_matrix_utils.h - internal/ceres/compressed_row_jacobian_writer.h - internal/ceres/compressed_row_sparse_matrix.h - internal/ceres/concurrent_queue.h - internal/ceres/conjugate_gradients_solver.h - internal/ceres/context_impl.h - internal/ceres/coordinate_descent_minimizer.h - internal/ceres/corrector.h - internal/ceres/covariance_impl.h - internal/ceres/cxsparse.h - internal/ceres/dense_jacobian_writer.h - internal/ceres/dense_normal_cholesky_solver.h - internal/ceres/dense_qr_solver.h - internal/ceres/dense_sparse_matrix.h - internal/ceres/detect_structure.h - internal/ceres/dogleg_strategy.h - internal/ceres/dynamic_compressed_row_finalizer.h - internal/ceres/dynamic_compressed_row_jacobian_writer.h - internal/ceres/dynamic_compressed_row_sparse_matrix.h - internal/ceres/dynamic_sparse_normal_cholesky_solver.h - internal/ceres/eigensparse.h - internal/ceres/evaluator.h - internal/ceres/execution_summary.h - internal/ceres/file.h - internal/ceres/float_cxsparse.h - internal/ceres/float_suitesparse.h - internal/ceres/function_sample.h - internal/ceres/gradient_checking_cost_function.h - internal/ceres/gradient_problem_evaluator.h - internal/ceres/graph_algorithms.h - internal/ceres/graph.h - internal/ceres/implicit_schur_complement.h - internal/ceres/inner_product_computer.h - internal/ceres/invert_psd_matrix.h - internal/ceres/is_close.h - internal/ceres/iterative_refiner.h - internal/ceres/iterative_schur_complement_solver.h - internal/ceres/lapack.h - internal/ceres/levenberg_marquardt_strategy.h - internal/ceres/linear_least_squares_problems.h - internal/ceres/linear_operator.h - internal/ceres/linear_solver.h - internal/ceres/line_search_direction.h - internal/ceres/line_search.h - internal/ceres/line_search_minimizer.h - internal/ceres/line_search_preprocessor.h - internal/ceres/low_rank_inverse_hessian.h - internal/ceres/map_util.h - internal/ceres/minimizer.h - internal/ceres/pair_hash.h - internal/ceres/parallel_for.h - internal/ceres/parallel_utils.h - internal/ceres/parameter_block.h - internal/ceres/parameter_block_ordering.h - internal/ceres/partitioned_matrix_view.h - internal/ceres/partitioned_matrix_view_impl.h - internal/ceres/polynomial.h - internal/ceres/preconditioner.h - internal/ceres/preprocessor.h - internal/ceres/problem_impl.h - internal/ceres/program_evaluator.h - internal/ceres/program.h - internal/ceres/random.h - internal/ceres/reorder_program.h - internal/ceres/residual_block.h - internal/ceres/residual_block_utils.h - internal/ceres/schur_complement_solver.h - internal/ceres/schur_eliminator.h - internal/ceres/schur_eliminator_impl.h - internal/ceres/schur_jacobi_preconditioner.h - internal/ceres/schur_templates.h - internal/ceres/scoped_thread_token.h - internal/ceres/scratch_evaluate_preparer.h - internal/ceres/single_linkage_clustering.h - internal/ceres/small_blas_generic.h - internal/ceres/small_blas.h - internal/ceres/solver_utils.h - internal/ceres/sparse_cholesky.h - internal/ceres/sparse_matrix.h - internal/ceres/sparse_normal_cholesky_solver.h - internal/ceres/split.h - internal/ceres/stl_util.h - internal/ceres/stringprintf.h - internal/ceres/subset_preconditioner.h - internal/ceres/suitesparse.h - internal/ceres/thread_pool.h - internal/ceres/thread_token_provider.h - internal/ceres/triplet_sparse_matrix.h - internal/ceres/trust_region_minimizer.h - internal/ceres/trust_region_preprocessor.h - internal/ceres/trust_region_step_evaluator.h - internal/ceres/trust_region_strategy.h - internal/ceres/visibility_based_preconditioner.h - internal/ceres/visibility.h - internal/ceres/wall_time.h + internal/ceres/generated/partitioned_matrix_view_d_d_d.cc + internal/ceres/generated/schur_eliminator_d_d_d.cc ) set(LIB @@ -302,48 +314,48 @@ set(LIB if(WITH_LIBMV_SCHUR_SPECIALIZATIONS) list(APPEND SRC - internal/ceres/generated/partitioned_matrix_view_2_2_2.cc - internal/ceres/generated/partitioned_matrix_view_2_2_3.cc - internal/ceres/generated/partitioned_matrix_view_2_2_4.cc - internal/ceres/generated/partitioned_matrix_view_2_2_d.cc - internal/ceres/generated/partitioned_matrix_view_2_3_3.cc - internal/ceres/generated/partitioned_matrix_view_2_3_4.cc - internal/ceres/generated/partitioned_matrix_view_2_3_6.cc - internal/ceres/generated/partitioned_matrix_view_2_3_9.cc - internal/ceres/generated/partitioned_matrix_view_2_3_d.cc - internal/ceres/generated/partitioned_matrix_view_2_4_3.cc - internal/ceres/generated/partitioned_matrix_view_2_4_4.cc - internal/ceres/generated/partitioned_matrix_view_2_4_6.cc - internal/ceres/generated/partitioned_matrix_view_2_4_8.cc - internal/ceres/generated/partitioned_matrix_view_2_4_9.cc - internal/ceres/generated/partitioned_matrix_view_2_4_d.cc - internal/ceres/generated/partitioned_matrix_view_2_d_d.cc - internal/ceres/generated/partitioned_matrix_view_3_3_3.cc - internal/ceres/generated/partitioned_matrix_view_4_4_2.cc - internal/ceres/generated/partitioned_matrix_view_4_4_3.cc - internal/ceres/generated/partitioned_matrix_view_4_4_4.cc - internal/ceres/generated/partitioned_matrix_view_4_4_d.cc - internal/ceres/generated/schur_eliminator_2_2_2.cc - internal/ceres/generated/schur_eliminator_2_2_3.cc - internal/ceres/generated/schur_eliminator_2_2_4.cc - internal/ceres/generated/schur_eliminator_2_2_d.cc - internal/ceres/generated/schur_eliminator_2_3_3.cc - internal/ceres/generated/schur_eliminator_2_3_4.cc - internal/ceres/generated/schur_eliminator_2_3_6.cc - internal/ceres/generated/schur_eliminator_2_3_9.cc - internal/ceres/generated/schur_eliminator_2_3_d.cc - internal/ceres/generated/schur_eliminator_2_4_3.cc - internal/ceres/generated/schur_eliminator_2_4_4.cc - internal/ceres/generated/schur_eliminator_2_4_6.cc - internal/ceres/generated/schur_eliminator_2_4_8.cc - internal/ceres/generated/schur_eliminator_2_4_9.cc - internal/ceres/generated/schur_eliminator_2_4_d.cc - internal/ceres/generated/schur_eliminator_2_d_d.cc - internal/ceres/generated/schur_eliminator_3_3_3.cc - internal/ceres/generated/schur_eliminator_4_4_2.cc - internal/ceres/generated/schur_eliminator_4_4_3.cc - internal/ceres/generated/schur_eliminator_4_4_4.cc - internal/ceres/generated/schur_eliminator_4_4_d.cc + internal/ceres/generated/partitioned_matrix_view_2_2_2.cc + internal/ceres/generated/partitioned_matrix_view_2_2_3.cc + internal/ceres/generated/partitioned_matrix_view_2_2_4.cc + internal/ceres/generated/partitioned_matrix_view_2_2_d.cc + internal/ceres/generated/partitioned_matrix_view_2_3_3.cc + internal/ceres/generated/partitioned_matrix_view_2_3_4.cc + internal/ceres/generated/partitioned_matrix_view_2_3_6.cc + internal/ceres/generated/partitioned_matrix_view_2_3_9.cc + internal/ceres/generated/partitioned_matrix_view_2_3_d.cc + internal/ceres/generated/partitioned_matrix_view_2_4_3.cc + internal/ceres/generated/partitioned_matrix_view_2_4_4.cc + internal/ceres/generated/partitioned_matrix_view_2_4_6.cc + internal/ceres/generated/partitioned_matrix_view_2_4_8.cc + internal/ceres/generated/partitioned_matrix_view_2_4_9.cc + internal/ceres/generated/partitioned_matrix_view_2_4_d.cc + internal/ceres/generated/partitioned_matrix_view_2_d_d.cc + internal/ceres/generated/partitioned_matrix_view_3_3_3.cc + internal/ceres/generated/partitioned_matrix_view_4_4_2.cc + internal/ceres/generated/partitioned_matrix_view_4_4_3.cc + internal/ceres/generated/partitioned_matrix_view_4_4_4.cc + internal/ceres/generated/partitioned_matrix_view_4_4_d.cc + internal/ceres/generated/schur_eliminator_2_2_2.cc + internal/ceres/generated/schur_eliminator_2_2_3.cc + internal/ceres/generated/schur_eliminator_2_2_4.cc + internal/ceres/generated/schur_eliminator_2_2_d.cc + internal/ceres/generated/schur_eliminator_2_3_3.cc + internal/ceres/generated/schur_eliminator_2_3_4.cc + internal/ceres/generated/schur_eliminator_2_3_6.cc + internal/ceres/generated/schur_eliminator_2_3_9.cc + internal/ceres/generated/schur_eliminator_2_3_d.cc + internal/ceres/generated/schur_eliminator_2_4_3.cc + internal/ceres/generated/schur_eliminator_2_4_4.cc + internal/ceres/generated/schur_eliminator_2_4_6.cc + internal/ceres/generated/schur_eliminator_2_4_8.cc + internal/ceres/generated/schur_eliminator_2_4_9.cc + internal/ceres/generated/schur_eliminator_2_4_d.cc + internal/ceres/generated/schur_eliminator_2_d_d.cc + internal/ceres/generated/schur_eliminator_3_3_3.cc + internal/ceres/generated/schur_eliminator_4_4_2.cc + internal/ceres/generated/schur_eliminator_4_4_3.cc + internal/ceres/generated/schur_eliminator_4_4_4.cc + internal/ceres/generated/schur_eliminator_4_4_d.cc ) else() add_definitions(-DCERES_RESTRICT_SCHUR_SPECIALIZATION) @@ -351,16 +363,5 @@ endif() add_definitions(${GFLAGS_DEFINES}) add_definitions(${GLOG_DEFINES}) -add_definitions(${CERES_DEFINES}) - -add_definitions( - -DCERES_HAVE_PTHREAD - -DCERES_NO_SUITESPARSE - -DCERES_NO_CXSPARSE - -DCERES_NO_LAPACK - -DCERES_NO_ACCELERATE_SPARSE - -DCERES_HAVE_RWLOCK - -DCERES_USE_CXX_THREADS -) blender_add_lib(extern_ceres "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/extern/ceres/ChangeLog b/extern/ceres/ChangeLog deleted file mode 100644 index 40fe3f16bac..00000000000 --- a/extern/ceres/ChangeLog +++ /dev/null @@ -1,596 +0,0 @@ -commit 399cda773035d99eaf1f4a129a666b3c4df9d1b1 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Fri Oct 23 19:36:08 2020 +0100 - - Update build documentation to reflect detection of Eigen via config mode - - Change-Id: I18d5f0fc1eb51ea630164c911d935e9bffea35ce - -commit bb127272f9b57672bca48424f2d83bc430a46eb8 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Oct 19 09:28:34 2020 -0700 - - Fix typos. - - Contributed by Ishamis@, IanBoyanZhang@, gkrobner@ & mithunjacob@. - - Change-Id: Iab3c19a07a6f3db2486e3557dcb55bfe5de2aee5 - -commit a0ec5c32af5c5f5a52168dc2748be910dba14810 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Sun Oct 18 15:20:36 2020 -0700 - - Update version history for 2.0.0RC2 - - Change-Id: I75b7515fbf9880bd8eaea6ecd5e72ce1ae4a3a86 - -commit 3f6d2736769044e7c08c873c41a184849eea73ab -Author: Taylor Braun-Jones <taylor@braun-jones.org> -Date: Tue Jan 28 12:09:30 2020 -0500 - - Unify symbol visibility configuration for all compilers - - This makes it possible to build unit tests with shared libraries on MSVC. - - Change-Id: I1db66a80b2c78c4f3d354e35235244d17bac9809 - -commit 29c2912ee635c77f3ddf2e382a5d6a9cf9805a3d -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Oct 13 12:07:06 2020 -0700 - - Unbreak the bazel build some more - - Change-Id: I6bbf3df977a473b9b5e16a9e59da5f535f8cdc24 - -commit bf47e1a36829f62697b930241d0a353932f34090 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Oct 13 10:00:22 2020 -0700 - - Fix the Bazel build. - - 1. Fix the path to eigen, now that it uses gitlab instead of bitbucket. - 2. Remove an unrecognized compiler option. - 3. Remove an obsolete benchmark. - - This CL only unbreaks the build, it is likely that it is still not - at par with the cmake build. - - https://github.com/ceres-solver/ceres-solver/issues/628 - - Change-Id: I470209cbb48b6a4f499564a86b52436e0c8d98ef - -commit 600e8c529ebbb4bb89d5baefa3d5ab6ad923706a -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Mon Oct 12 23:00:39 2020 +0200 - - fix minor typos - - all timing values in the summary are initialized to -1, so the one - +1 is likely an oversight. - - Change-Id: Ie355f3b7da08a56d49d19ca9a5bc48fe5581dee3 - -commit bdcdcc78af61a0cb85317ebee52dc804bf4ea975 -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Mon Sep 7 01:48:50 2020 +0200 - - update docs for changed cmake usage - - - update links to cmake docs to version 3.5 - - highlight difference between dependencies with and without custom - find modules - - point out removal of CERES_INCLUDE_DIRS - - point out that TBB might be linked if SuiteSparseQR is found - - added 'Migration' section - - fixed typos - - Change-Id: Icbcc0e723d11f12246fb3cf09b9d7c6206195a82 - -commit 3f69e5b36a49b44344e96a26b39693a914ba80c6 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Oct 12 11:46:40 2020 -0700 - - Corrections from William Rucklidge - - Change-Id: I0b5d4808be48f68df7829c70ec93ffa67d81315d - -commit 8bfdb02fb18551bbd5f222c5472e45eddecd42b9 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Oct 12 10:07:13 2020 -0700 - - Rewrite uses of VLOG_IF and LOG_IF. - - VLOG_IF's evaluation order is ambiguous - does it mean - `if (cond) VLOG(lvl)` or `if (VLOG_IS_ON(lvl) && cond) LOG(INFO)`? - In particular, the way it works now is inconsistent with the way the - rest of the LOG macros evaluate their arguments. - Fixing this would be hard, and the macro's behavior would still surprise - some people. Replacing it with an if statement is simple, clear, and unambiguous. - - Change-Id: I97a92d17a932c0a5344a1bf98d676308793ba877 - -commit d1b35ffc161fd857c7c433574ca82aa9b2db7f98 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Oct 12 10:58:05 2020 -0700 - - Corrections from William Rucklidge - - Change-Id: Ifb50e87aa915d00f9861fe1a6da0acee11bc0a94 - -commit f34e80e91f600014a3030915cf9ea28bcbc576e7 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Thu Oct 8 12:34:53 2020 -0700 - - Add dividers between licenses. - - Change-Id: I4e4aaa15e0621c5648550cfa622fe0a79f1f4f9f - -commit 65c397daeca77da53d16e73720b9a17edd6757ab -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Wed Oct 7 14:34:52 2020 -0700 - - Fix formatting - - Change-Id: Ib4ca8a097059dbb8d2f3a6a888222c0188cb126e - -commit f63b1fea9cfa48ae4530c327b10efa4985e69631 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Wed Oct 7 14:30:54 2020 -0700 - - Add the MIT license text corresponding to the libmv derived files. - - Change-Id: Ie72fb45ae96a7892c00411eee6873db7f0e365a8 - -commit 542613c13d8b7469822aff5eec076f2cad4507ec -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Tue Oct 6 22:48:59 2020 +0200 - - minor formatting fix for trust_region_minimizer.cc - - Change-Id: I18ba27825fc23dd0e9e3e15dc13fc0833db01b5b - -commit 6d9e9843d8c61cfb04cc55b9def9518f823a592a -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Sep 28 11:35:37 2020 -0700 - - Remove inclusion of ceres/eigen.h - - The initial reason for this is because of a previous reformatting CL - triggered a macro redefinition warning in the schur eliminator. But - actually it was worse because the reordering had caused the macro - definition to be ignored and caused a performance regression. - - This simplifies the generated files, fixes some formatting errors - and recovers the performance. - - Change-Id: I9dbeffc38743b3f24b25843feec2e26a73188413 - -commit eafeca5dcb7af8688d40a9c14b0d2fcb856c96fc -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Sep 28 11:12:59 2020 -0700 - - Fix a logging bug in TrustRegionMinimizer. - - Upon encountering an unsuccessful step (one where the cost goes up) - the the trust region minimizer failed to populate the gradient norm - in the IterationSummary. This would cause the gradient norm to be - logged as zero which is incorrect. Instead it should be the gradient - norm at the current point. - - This CL fixes this issue. - - Before: - iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time - 0 1.115206e+07 0.00e+00 1.90e+07 0.00e+00 0.00e+00 1.00e+04 0 2.72e-01 1.33e+00 - 1 3.687552e+06 7.46e+06 1.84e+08 2.86e+03 6.91e-01 1.06e+04 1 1.32e+00 2.65e+00 - 2 3.670266e+10 -3.67e+10 0.00e+00 3.27e+03 -1.07e+04 5.30e+03 1 7.52e-01 3.40e+00 - 3 4.335397e+07 -3.97e+07 0.00e+00 2.74e+03 -1.16e+01 1.32e+03 1 7.28e-01 4.13e+00 - 4 1.345488e+06 2.34e+06 4.12e+07 1.55e+03 6.87e-01 1.40e+03 1 9.31e-01 5.06e+00 - 5 5.376653e+05 8.08e+05 9.99e+06 6.64e+02 7.46e-01 1.59e+03 1 9.64e-01 6.03e+00 - - After: - iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time - 0 1.115206e+07 0.00e+00 1.90e+07 0.00e+00 0.00e+00 1.00e+04 0 2.37e-01 1.13e+00 - 1 3.687552e+06 7.46e+06 1.84e+08 2.86e+03 6.91e-01 1.06e+04 1 1.08e+00 2.21e+00 - 2 3.670266e+10 -3.67e+10 1.84e+08 3.27e+03 -1.07e+04 5.30e+03 1 7.50e-01 2.96e+00 - 3 4.335397e+07 -3.97e+07 1.84e+08 2.74e+03 -1.16e+01 1.32e+03 1 7.13e-01 3.67e+00 - 4 1.345488e+06 2.34e+06 4.12e+07 1.55e+03 6.87e-01 1.40e+03 1 9.01e-01 4.57e+00 - 5 5.376653e+05 8.08e+05 9.99e+06 6.64e+02 7.46e-01 1.59e+03 1 9.36e-01 5.51e+00 - - Change-Id: Iae538fe089be07c7bb219337a6f1392f7213acfe - -commit 1fd0be916dd4ff4241bd52264b9e9170bc7e4339 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Mon Sep 28 18:54:33 2020 +0100 - - Fix default initialisation of IterationCallback::cost - - Change-Id: I9f529093fc09424c90dbff8e9648b90b16990623 - -commit 137bbe845577929a87f8eef979196df6a8b30ee4 -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Mon Sep 28 02:17:32 2020 +0200 - - add info about clang-format to contributing docs - - Change-Id: I2f4dcbda2e4f36096df217d76de370103ffaa43e - -commit d3f66d77f45482b90d01af47938289c32dd2cc08 -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Mon Sep 28 02:01:43 2020 +0200 - - fix formatting generated files (best effort) - - - update file generator scripts / templates so generated files adhere - to clang-format - - A few exceptions are not fixed, where the file generation results in - lines of different width. To properly fix this would make the code - more complicated and it's not that important for generated files - anyway. - - note that generated files are excluded in ./scripts/format_all.sh - - Change-Id: I4f42c83d1fec01242eada5e7ce6c1a5192234d37 - -commit a9c7361c8dc1d37e78d216754a4c03e8a8f1e74f -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Mon Sep 28 02:14:29 2020 +0200 - - minor formatting fix (wrongly updated in earlier commit) - - Change-Id: I544635fd936cb5b7f7bd9255876641cd5a9590c6 - -commit 7b8f675bfdb1d924af6a2dcc1f79bda5ace7e886 -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Sun Sep 20 21:45:24 2020 +0200 - - fix formatting for (non-generated) internal source files - - - Change formatting standard to Cpp11. Main difference is not having - the space between two closing >> for nested templates. We don't - choose c++14, because older versions of clang-format (version 9 - and earlier) don't know this value yet, and it doesn't make a - difference in the formatting. - - Apply clang-format to all (non generated) internal source files. - - Manually fix some code sections (clang-format on/off) and c-strings - - Exclude some embedded external files with very different formatting - (gtest/gmock) - - Add script to format all source files - - Change-Id: Ic6cea41575ad6e37c9e136dbce176b0d505dc44d - -commit 921368ce31c42ee793cf131860abba291a7e39ad -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Wed Sep 9 09:15:37 2020 -0700 - - Fix a number of typos in covariance.h - - Also some minor cleanups in covariance_impl.h - - Thanks to Lorenzo Lamia for pointing these out. - - Change-Id: Icb4012a367fdd1f249bc1e7019e0114c868e45b6 - -commit 7b6b2491cc1be0b3abb67338366d8d69bef3a402 -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Tue Sep 8 17:51:32 2020 +0200 - - fix formatting for examples - - This is mostly just applying the existing clang format config, except: - - Use NOLINT on overlong comment lines. - - Wrap some sections in 'clang-format off' / 'clang format on'. - - Manually split or join some multi-line strings. - - Change-Id: Ia1a40eeb92112e12c3a169309afe087af55b2f4f - -commit 82275d8a4eac4fc0bd07e17c3a41a6e429e72bfb -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Tue Sep 8 02:00:21 2020 +0200 - - some fixes for Linux and macOS install docs - - Linux: - - Remove workaround for Ubuntu 14.04, which is EOL. libsuitesparse-dev - seems to come with a shared library on 16.04 and later, so linking - to a shared build of ceres doesn't seem to be an issue any more. - - Add missing libgflags-dev. - - macOS: - - OS X is now called macOS. - - Update homebrew link. - - Mac homebrew the preferred method of installation. - - Fix OpenMP instructions. - - Remove reference to homebrew/science. Everything is in core. - - Add missing gflags. - - Change-Id: I633b3c7ea84a87886bfd823f8187fdd0a84737c9 - -commit 9d762d74f06b946bbd2f098de7216032d0e7b51d -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Sun Sep 6 21:04:24 2020 +0200 - - fix formatting for public header files - - - ensure all public headers files adhere to clang-format - - preserve one-per-line for enums by adding trailing comma - - preserve include order for en/disable_warning.h - - Change-Id: I78dbd0527a294ab2ec5f074fb426e48b20c393e6 - -commit c76478c4898f3af11a6a826ac89c261205f4dd96 -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Sun Sep 6 23:29:56 2020 +0200 - - gitignore *.pyc - - Change-Id: Ic6238a617a3c7ce92df7dcefcc44bae20c32b30b - -commit 4e69a475cd7d7cbed983f5aebf79ae13a46e5415 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Tue Sep 1 10:15:23 2020 +0100 - - Fix potential for mismatched release/debug TBB libraries - - - Protect against the case when the user has multiple installs of TBB - in their search paths and the first install does not contain debug - libraries. In this case it is possible to get mismatched versions - of TBB inserted into TBB_LIBRARIES. - - Also suppresses warning about use of TBB_ROOT on modern versions of - CMake due to CMP0074. - - Change-Id: I2eaafdde4a028cbf6c500c63771973d85bc4723d - -commit 8e1d8e32ad0d28c0d4d1d7b2b1ce7fc01d90b7b0 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Thu Sep 3 10:49:20 2020 -0700 - - A number of small changes. - - 1. Add a move constructor to NumericDiffCostFunction, DynamicAutoDiffCostfunction - and DynamicNumericDiffCostFunction. - 2. Add optional ownership of the underlying functor. - 3. Update docs to reflect this as well as the variadic templates that allow an - arbitrary number of parameter blocks. - - Change-Id: I57bbb51fb9e75f36ec2a661b603beda270f30a19 - -commit 368a738e5281039f19587545806b7bc6f35e78f9 -Author: Julian Kent <jkflying@gmail.com> -Date: Thu May 7 12:54:35 2020 +0200 - - AutoDiffCostFunction: optional ownership - - Add Ownership semantics to the AutoDiffCostFunction - - This allows several benefits, such as pointer ordering always being the - same for numerical repeatability (due to blocks being ordered by - pointer address), memory adjacency for better cache performance, and - reduced allocator pressure / overhead. - - This is then made use of in libmv by preallocating the errors and - cost functions into vectors - - Change-Id: Ia5b97e7249b55a463264b6e26f7a02291927c9f2 - -commit 8cbd721c199c69f127af6ef7c187ddf7e8f116f9 -Author: Morten Hannemose <morten@hannemose.dk> -Date: Thu Sep 3 17:54:20 2020 +0200 - - Add erf and erfc to jet.h, including tests in jet_test.cc - - erf is necessary for evaluating Gaussian functions. - erfc was added because it is so similar to erf. - - Change-Id: I5e470dbe013cc938fabb87cde3b0ebf26a90fff4 - -commit 31366cff299cf2a8d97b43a7533d953ff28fdc29 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Sep 1 09:23:34 2020 -0700 - - Benchmarks for dynamic autodiff. - - This patch is from Clement Courbet. courbet@google.com - - Change-Id: I886390663644733bfa5b7b52b0c883079e793726 - -commit 29fb08aeae1ce691851724af7209fea6127523a9 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Tue Sep 1 10:23:31 2020 +0100 - - Use CMAKE_PREFIX_PATH to pass Homebrew install location - - - Passing HINTS disables the MODULE mode of find_package() which - precludes users from creating their own find modules to provide - Ceres' dependencies. - - Change-Id: I6f2edf429331d13fe67bf61ac4b79d17579d9a57 - -commit 242c703b501ffd64d645f4016d63c8b41c381038 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Aug 4 21:02:11 2020 -0700 - - Minor fixes to the documentation - - Change-Id: I65e6f648d963b8aa640078684ce02dcde6acb87d - -commit 79bbf95103672fa4b5485e055ff7692ee4a1f9da -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Aug 4 18:26:02 2020 -0700 - - Add changelog for 2.0.0 - - Change-Id: I8acad62bfe629454ae5032732693e43fe37b97ff - -commit 41d05f13d0ffb230d7a5a9d67ed31b0cfb35d669 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Aug 4 14:56:33 2020 -0700 - - Fix lint errors in evaluation_callback_test.cc - - Change-Id: I63eb069544ad0d8f495490fe4caa07b9f04f7ec2 - -commit 4b67903c1f96037048c83a723028c5d0991c09cf -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Aug 4 14:40:50 2020 -0700 - - Remove unused variables from problem_test.cc - - Change-Id: Ia1a13cfc6e462f6d249dcbf169ad34831dd93ec2 - -commit 10449fc3664c96d4b5454c092195432df79412f8 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Aug 4 14:30:25 2020 -0700 - - Add Apache license to the LICENSE file for FixedArray - - FixedArray implementation comes from ABSL which is Apache - licensed. - - Change-Id: I566dbe9d236814c95945732c6347d3bf7b508283 - -commit 8c3ecec6db26d7a66f5de8dc654475ec7aa0df14 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue May 26 04:44:11 2020 -0700 - - Fix some minor errors in IterationCallback docs - - Change-Id: Id3d7f21a523ff8466868cdec542921c566bbbfa9 - -commit 7d3ffcb4234632dc51ee84c8a509d9428263070b -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sun Jul 26 19:42:16 2020 +0100 - - Remove forced CONFIG from find_package(Eigen3) - - - Ceres will fail to configure if Eigen3::Eigen target is not found, and - the minimum required Eigen version specified (3.3) exports Eigen as - a CMake package and this is reflected in the default Ubuntu 18.04 - packages. - - This permits users to specify their own Eigen3 detection should they - choose to do so, but they must do so via an imported target. - - Change-Id: I5edff117c8001770004f49012ac1ae63b66ec9c1 - -commit a029fc0f93817f20b387b707bc578dc1f1a269ae -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sun Jul 26 18:44:59 2020 +0100 - - Use latest FindTBB.cmake from VTK project - - - Retrieved from [1], SHA: 0d9bbf9beb97f8f696c43a9edf1e52c082b3639b on - 2020-07-26 - - [1]: https://gitlab.kitware.com/vtk/vtk/blob/master/CMake/FindTBB.cmake - - Change-Id: I953a8c87802a974d30ccc7c80f5229683826efbd - -commit aa1abbc578797c6b17ee7221db31535dc249ae66 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sun Jul 26 19:57:31 2020 +0100 - - Replace use of GFLAGS_LIBRARIES with export gflags target - - - As our minimum required version of gflags (2.2) exports itself as - a CMake package and this is the case for the default 18.04 package - we can use the gflags target directly. - - Replaces forced use of CONFIG in find_package(gflags) with a check - that the gflags imported target exists to avoid ambiguity with - libgflags if installed in a default location. This permits users to - override the gflags detection should they so choose, provided that - they do so via an imported target. - - Also removes some previously removed legacy GLAGS_ vars from the - installation docs. - - Change-Id: I015f5a751e5b22f956bbf9df692e63a6825c9f0d - -commit db2af1be8780bbe88944775400baa2dbd3592b7d -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Aug 3 04:57:08 2020 -0700 - - Add Problem::EvaluateResidualBlockAssumingParametersUnchanged - - Simplify the semantics for Problem::EvaluateResidualBlock to - not ignore the presence of EvaluationCallback and add another method - EvaluateResidualBlockAssumingParametersUnchanged to handle the case - where the user has an EvaluationCallback but knows that the parameter - blocks do not change between calls. - - Updated the documentation for the methods and EvaluationCallback to - reflect these semantics. - - Also added tests for Evaluation related methods calling i - EvaluationCallback when its present. - - https://github.com/ceres-solver/ceres-solver/issues/483 - - Change-Id: If0a0c95c2f1f92e9183a90df240104a69a71c46d - -commit ab4ed32cda004befd29a0b4b02f1d907e0c4dab7 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Aug 3 04:17:33 2020 -0700 - - Replace NULL with nullptr in the documentation. - - Change-Id: I995f68770e2a4b6027c0a1d3edf5eb5132b081d7 - -commit ee280e27a6140295ef6258d24c92305628f3d508 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Fri Jul 31 16:48:06 2020 -0700 - - Allow SubsetParameterization to accept an empty vector of constant parameters. - - Thanks to Frédéric Devernay for reporting this and providing an initial fix. - - Change-Id: Id86a2051ab7841ecafdcfb00f4634b353a7ef3b4 - -commit 4b8c731d8a4f3fda53c642ff14a25fab6c233918 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Fri Jul 31 10:05:52 2020 -0700 - - Fix a bug in DynamicAutoDiffCostFunction - - DynamicAutoDiffCostFunction::Evaluate when provided with a jacobians - array that was non-empty but all its entries are nullptr, would - compute num_active_parameters = 0, and then skip over all the loops - that evaluated the CostFunctor. - - The fix is to check if num_active_parameters == 0, and then treat - it as the case where jacobians array is null. - - Thanks to Ky Waegel for reporting and providing a reproduction for this. - - Change-Id: Ib86930c2c3f722724d249f662bf88238679bbf98 - -commit 5cb5b35a930c1702278083c75769dbb4e5801045 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sun Jul 26 20:42:12 2020 +0100 - - Fixed incorrect argument name in RotationMatrixToQuaternion() - - - Raised as: https://github.com/ceres-solver/ceres-solver/pull/607 by - Frank Dellaert - - Change-Id: Id3e9f190e814cf18206e2f8c3b1b67b995c21dd5 - -commit e39d9ed1d60dfeb58dd2a0df4622c683f87b28e3 -Author: Carl Dehlin <carl@dehlin.com> -Date: Tue Jun 16 09:02:05 2020 +0200 - - Add a missing term and remove a superfluous word - - Change-Id: I25f40f0bf241302b975e6fc14690aa863c0728b0 - -commit 27cab77b699a1a2b5354820c57a91c92eaeb21e3 -Author: Carl Dehlin <carl@dehlin.com> -Date: Mon Jun 15 20:01:18 2020 +0200 - - Reformulate some sentences - - Change-Id: I4841aa8e8522008dd816261d9ad98e5fb8ad1758 - -commit 8ac6655ce85a4462f2882fcb9e9118a7057ebe09 -Author: Carl Dehlin <carl@dehlin.com> -Date: Mon Jun 15 19:10:12 2020 +0200 - - Fix documentation formatting issues - - Change-Id: Iea3a6e75dc3a7376eda866ab24e535a6df84f8ea diff --git a/extern/ceres/LICENSE b/extern/ceres/LICENSE index 2e3ead5ed45..cf69df2e02f 100644 --- a/extern/ceres/LICENSE +++ b/extern/ceres/LICENSE @@ -1,6 +1,6 @@ Ceres Solver - A fast non-linear least squares minimizer -Copyright 2010, 2011, 2012 Google Inc. All rights reserved. -http://code.google.com/p/ceres-solver/ +Copyright 2015 Google Inc. All rights reserved. +http://ceres-solver.org/ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -25,3 +25,233 @@ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------------------------------------------------------------- + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +------------------------------------------------------------------------------------------------------------------------------- + +Some of the code in the examples directory derives from libmv, which is +distributed under the MIT license as described below + + +Copyright (c) 2007-2011 libmv authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/extern/ceres/README b/extern/ceres/README deleted file mode 100644 index 8dd8ccf91a1..00000000000 --- a/extern/ceres/README +++ /dev/null @@ -1,3 +0,0 @@ -Ceres Solver - A non-linear least squares minimizer -================================================== -Please see ceres.pdf in docs/ for a tutorial and reference. diff --git a/extern/ceres/README.blender b/extern/ceres/README.blender index 20e5dda6f57..8d43f8d16c5 100644 --- a/extern/ceres/README.blender +++ b/extern/ceres/README.blender @@ -1,5 +1,5 @@ Project: Ceres Solver URL: http://ceres-solver.org/ License: BSD 3-Clause -Upstream version 2.0.0 +Upstream version 2.1.0 Local modifications: None diff --git a/extern/ceres/README.md b/extern/ceres/README.md new file mode 100644 index 00000000000..7de8f0deed0 --- /dev/null +++ b/extern/ceres/README.md @@ -0,0 +1,18 @@ +[![Android](https://github.com/ceres-solver/ceres-solver/actions/workflows/android.yml/badge.svg)](https://github.com/ceres-solver/ceres-solver/actions/workflows/android.yml) +[![Linux](https://github.com/ceres-solver/ceres-solver/actions/workflows/linux.yml/badge.svg)](https://github.com/ceres-solver/ceres-solver/actions/workflows/linux.yml) +[![macOS](https://github.com/ceres-solver/ceres-solver/actions/workflows/macos.yml/badge.svg)](https://github.com/ceres-solver/ceres-solver/actions/workflows/macos.yml) +[![Windows](https://github.com/ceres-solver/ceres-solver/actions/workflows/windows.yml/badge.svg)](https://github.com/ceres-solver/ceres-solver/actions/workflows/windows.yml) + +Ceres Solver +============ + +Ceres Solver is an open source C++ library for modeling and solving +large, complicated optimization problems. It is a feature rich, mature +and performant library which has been used in production at Google +since 2010. Ceres Solver can solve two kinds of problems. + +1. Non-linear Least Squares problems with bounds constraints. +2. General unconstrained optimization problems. + +Please see [ceres-solver.org](http://ceres-solver.org/) for more +information. diff --git a/extern/ceres/bundle.sh b/extern/ceres/bundle.sh deleted file mode 100755 index e2d2dce8779..00000000000 --- a/extern/ceres/bundle.sh +++ /dev/null @@ -1,165 +0,0 @@ -#!/bin/sh - -if [ "x$1" = "x--i-really-know-what-im-doing" ] ; then - echo Proceeding as requested by command line ... -else - echo "*** Please run again with --i-really-know-what-im-doing ..." - exit 1 -fi - -repo="https://ceres-solver.googlesource.com/ceres-solver" -#branch="master" -tag="2.0.0" -tmp=`mktemp -d` -checkout="$tmp/ceres" - -GIT="git --git-dir $tmp/ceres/.git --work-tree $checkout" - -git clone $repo $checkout - -if [ $branch != "master" ]; then - $GIT checkout -t remotes/origin/$branch -else - if [ "x$tag" != "x" ]; then - $GIT checkout $tag - fi -fi - -$GIT log -n 50 > ChangeLog - -for p in `cat ./patches/series`; do - echo "Applying patch $p..." - cat ./patches/$p | patch -d $tmp/ceres -p1 -done - -find include -type f -not -iwholename '*.svn*' -exec rm -rf {} \; -find internal -type f -not -iwholename '*.svn*' -exec rm -rf {} \; - -cat "files.txt" | while read f; do - mkdir -p `dirname $f` - cp $tmp/ceres/$f $f -done - -rm -rf $tmp - -sources=`find ./include ./internal -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | sed -r 's/^\.\//\t/' | \ - grep -v -E 'schur_eliminator_[0-9]_[0-9d]_[0-9d].cc' | \ - grep -v -E 'partitioned_matrix_view_[0-9]_[0-9d]_[0-9d].cc' | sort -d` -generated_sources=`find ./include ./internal -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | sed -r 's/^\.\//\t\t/' | \ - grep -E 'schur_eliminator_[0-9]_[0-9d]_[0-9d].cc|partitioned_matrix_view_[0-9]_[0-9d]_[0-9d].cc' | sort -d` -headers=`find ./include ./internal -type f -iname '*.h' | sed -r 's/^\.\//\t/' | sort -d` - -src_dir=`find ./internal -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | sed -r 's/^\.\//\t/' | sort -d | uniq` -src="" -for x in $src_dir $src_third_dir; do - t="" - - if test `echo "$x" | grep -c glog ` -eq 1; then - continue; - fi - - if test `echo "$x" | grep -c generated` -eq 1; then - continue; - fi - - if stat $x/*.cpp > /dev/null 2>&1; then - t="src += env.Glob('`echo $x'/*.cpp'`')" - fi - - if stat $x/*.c > /dev/null 2>&1; then - if [ -z "$t" ]; then - t="src += env.Glob('`echo $x'/*.c'`')" - else - t="$t + env.Glob('`echo $x'/*.c'`')" - fi - fi - - if stat $x/*.cc > /dev/null 2>&1; then - if [ -z "$t" ]; then - t="src += env.Glob('`echo $x'/*.cc'`')" - else - t="$t + env.Glob('`echo $x'/*.cc'`')" - fi - fi - - if [ -z "$src" ]; then - src=$t - else - src=`echo "$src\n$t"` - fi -done - -cat > CMakeLists.txt << EOF -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# 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) 2012, Blender Foundation -# All rights reserved. -# ***** END GPL LICENSE BLOCK ***** - -# NOTE: This file is automatically generated by bundle.sh script -# If you're doing changes in this file, please update template -# in that script too - -set(INC - . - include - internal - config - ../gflags/src -) - -set(INC_SYS - \${EIGEN3_INCLUDE_DIRS} - \${GFLAGS_INCLUDE_DIRS} - \${GLOG_INCLUDE_DIRS} -) - -set(SRC -${sources} - -${headers} -) - -set(LIB - \${GLOG_LIBRARIES} - \${GFLAGS_LIBRARIES} -) - -if(WITH_LIBMV_SCHUR_SPECIALIZATIONS) - list(APPEND SRC -${generated_sources} - ) -else() - add_definitions(-DCERES_RESTRICT_SCHUR_SPECIALIZATION) -endif() - -add_definitions(\${GFLAGS_DEFINES}) -add_definitions(\${GLOG_DEFINES}) -add_definitions(\${CERES_DEFINES}) - -add_definitions( - -DCERES_HAVE_PTHREAD - -DCERES_NO_SUITESPARSE - -DCERES_NO_CXSPARSE - -DCERES_NO_LAPACK - -DCERES_NO_ACCELERATE_SPARSE - -DCERES_HAVE_RWLOCK - -DCERES_USE_CXX_THREADS -) - -blender_add_lib(extern_ceres "\${SRC}" "\${INC}" "\${INC_SYS}" "\${LIB}") -EOF diff --git a/extern/ceres/config/ceres/internal/config.h b/extern/ceres/config/ceres/internal/config.h index 1cf034ded5f..2566945e084 100644 --- a/extern/ceres/config/ceres/internal/config.h +++ b/extern/ceres/config/ceres/internal/config.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -28,21 +28,98 @@ // // Author: alexs.mac@gmail.com (Alex Stewart) -// Default (empty) configuration options for Ceres. +// Configuration options for Ceres. // -// IMPORTANT: Most users of Ceres will not use this file, when -// compiling Ceres with CMake, CMake will configure a new -// config.h with the currently selected Ceres compile -// options in <BUILD_DIR>/config, which will be added to -// the include path for compilation, and installed with the -// public Ceres headers. However, for some users of Ceres -// who compile without CMake (Android), this file ensures -// that Ceres will compile, with the user either specifying -// manually the Ceres compile options, or passing them -// directly through the compiler. +// Do not edit this file, it was automatically configured by CMake when +// Ceres was compiled with the relevant configuration for the machine +// on which Ceres was compiled. +// +// Ceres Developers: All options should have the same name as their mapped +// CMake options, in the preconfigured version of this file +// all options should be enclosed in '@'. #ifndef CERES_PUBLIC_INTERNAL_CONFIG_H_ #define CERES_PUBLIC_INTERNAL_CONFIG_H_ +// If defined, use the LGPL code in Eigen. +#define CERES_USE_EIGEN_SPARSE + +// If defined, Ceres was compiled without LAPACK. +#define CERES_NO_LAPACK + +// If defined, Ceres was compiled without SuiteSparse. +#define CERES_NO_SUITESPARSE + +// If defined, Ceres was compiled without CXSparse. +#define CERES_NO_CXSPARSE + +// If defined, Ceres was compiled without CUDA. +#define CERES_NO_CUDA + +// If defined, Ceres was compiled without Apple's Accelerate framework solvers. +#define CERES_NO_ACCELERATE_SPARSE + +#if defined(CERES_NO_SUITESPARSE) && \ + defined(CERES_NO_ACCELERATE_SPARSE) && \ + defined(CERES_NO_CXSPARSE) && \ + !defined(CERES_USE_EIGEN_SPARSE) // NOLINT +// If defined Ceres was compiled without any sparse linear algebra support. +#define CERES_NO_SPARSE +#endif + +// If defined, Ceres was compiled without Schur specializations. +// #define CERES_RESTRICT_SCHUR_SPECIALIZATION + +// If defined, Ceres was compiled to use Eigen instead of hardcoded BLAS +// routines. +// #define CERES_NO_CUSTOM_BLAS + +// If defined, Ceres was compiled without multithreading support. +// #define CERES_NO_THREADS +// If defined Ceres was compiled with OpenMP multithreading. +// #define CERES_USE_OPENMP +// If defined Ceres was compiled with modern C++ multithreading. +#define CERES_USE_CXX_THREADS + +// If defined, Ceres was compiled with a version MSVC >= 2005 which +// deprecated the standard POSIX names for bessel functions, replacing them +// with underscore prefixed versions (e.g. j0() -> _j0()). +#ifdef _MSC_VER +#define CERES_MSVC_USE_UNDERSCORE_PREFIXED_BESSEL_FUNCTIONS +#endif + +#if defined(CERES_USE_OPENMP) +#if defined(CERES_USE_CXX_THREADS) || defined(CERES_NO_THREADS) +#error CERES_USE_OPENMP is mutually exclusive to CERES_USE_CXX_THREADS and CERES_NO_THREADS +#endif +#elif defined(CERES_USE_CXX_THREADS) +#if defined(CERES_USE_OPENMP) || defined(CERES_NO_THREADS) +#error CERES_USE_CXX_THREADS is mutually exclusive to CERES_USE_OPENMP, CERES_USE_CXX_THREADS and CERES_NO_THREADS +#endif +#elif defined(CERES_NO_THREADS) +#if defined(CERES_USE_OPENMP) || defined(CERES_USE_CXX_THREADS) +#error CERES_NO_THREADS is mutually exclusive to CERES_USE_OPENMP and CERES_USE_CXX_THREADS +#endif +#else +# error One of CERES_USE_OPENMP, CERES_USE_CXX_THREADS or CERES_NO_THREADS must be defined. +#endif + +// CERES_NO_SPARSE should be automatically defined by config.h if Ceres was +// compiled without any sparse back-end. Verify that it has not subsequently +// been inconsistently redefined. +#if defined(CERES_NO_SPARSE) +#if !defined(CERES_NO_SUITESPARSE) +#error CERES_NO_SPARSE requires CERES_NO_SUITESPARSE. +#endif +#if !defined(CERES_NO_CXSPARSE) +#error CERES_NO_SPARSE requires CERES_NO_CXSPARSE +#endif +#if !defined(CERES_NO_ACCELERATE_SPARSE) +#error CERES_NO_SPARSE requires CERES_NO_ACCELERATE_SPARSE +#endif +#if defined(CERES_USE_EIGEN_SPARSE) +#error CERES_NO_SPARSE requires !CERES_USE_EIGEN_SPARSE +#endif +#endif #endif // CERES_PUBLIC_INTERNAL_CONFIG_H_ diff --git a/extern/ceres/config/ceres/internal/export.h b/extern/ceres/config/ceres/internal/export.h new file mode 100644 index 00000000000..c85bc5ca65d --- /dev/null +++ b/extern/ceres/config/ceres/internal/export.h @@ -0,0 +1,42 @@ + +#ifndef CERES_EXPORT_H +#define CERES_EXPORT_H + +#ifdef CERES_STATIC_DEFINE +# define CERES_EXPORT +# define CERES_NO_EXPORT +#else +# ifndef CERES_EXPORT +# ifdef ceres_EXPORTS + /* We are building this library */ +# define CERES_EXPORT +# else + /* We are using this library */ +# define CERES_EXPORT +# endif +# endif + +# ifndef CERES_NO_EXPORT +# define CERES_NO_EXPORT +# endif +#endif + +#ifndef CERES_DEPRECATED +# define CERES_DEPRECATED __attribute__ ((__deprecated__)) +#endif + +#ifndef CERES_DEPRECATED_EXPORT +# define CERES_DEPRECATED_EXPORT CERES_EXPORT CERES_DEPRECATED +#endif + +#ifndef CERES_DEPRECATED_NO_EXPORT +# define CERES_DEPRECATED_NO_EXPORT CERES_NO_EXPORT CERES_DEPRECATED +#endif + +#if 0 /* DEFINE_NO_DEPRECATED */ +# ifndef CERES_NO_DEPRECATED +# define CERES_NO_DEPRECATED +# endif +#endif + +#endif /* CERES_EXPORT_H */ diff --git a/extern/ceres/files.txt b/extern/ceres/files.txt deleted file mode 100644 index bfbd57090c9..00000000000 --- a/extern/ceres/files.txt +++ /dev/null @@ -1,318 +0,0 @@ -include/ceres/autodiff_cost_function.h -include/ceres/autodiff_first_order_function.h -include/ceres/autodiff_local_parameterization.h -include/ceres/c_api.h -include/ceres/ceres.h -include/ceres/conditioned_cost_function.h -include/ceres/context.h -include/ceres/cost_function.h -include/ceres/cost_function_to_functor.h -include/ceres/covariance.h -include/ceres/crs_matrix.h -include/ceres/cubic_interpolation.h -include/ceres/dynamic_autodiff_cost_function.h -include/ceres/dynamic_cost_function.h -include/ceres/dynamic_cost_function_to_functor.h -include/ceres/dynamic_numeric_diff_cost_function.h -include/ceres/evaluation_callback.h -include/ceres/first_order_function.h -include/ceres/gradient_checker.h -include/ceres/gradient_problem.h -include/ceres/gradient_problem_solver.h -include/ceres/internal/array_selector.h -include/ceres/internal/autodiff.h -include/ceres/internal/disable_warnings.h -include/ceres/internal/eigen.h -include/ceres/internal/fixed_array.h -include/ceres/internal/householder_vector.h -include/ceres/internal/integer_sequence_algorithm.h -include/ceres/internal/line_parameterization.h -include/ceres/internal/memory.h -include/ceres/internal/numeric_diff.h -include/ceres/internal/parameter_dims.h -include/ceres/internal/port.h -include/ceres/internal/reenable_warnings.h -include/ceres/internal/variadic_evaluate.h -include/ceres/iteration_callback.h -include/ceres/jet.h -include/ceres/local_parameterization.h -include/ceres/loss_function.h -include/ceres/normal_prior.h -include/ceres/numeric_diff_cost_function.h -include/ceres/numeric_diff_options.h -include/ceres/ordered_groups.h -include/ceres/problem.h -include/ceres/rotation.h -include/ceres/sized_cost_function.h -include/ceres/solver.h -include/ceres/tiny_solver_autodiff_function.h -include/ceres/tiny_solver_cost_function_adapter.h -include/ceres/tiny_solver.h -include/ceres/types.h -include/ceres/version.h -internal/ceres/accelerate_sparse.cc -internal/ceres/accelerate_sparse.h -internal/ceres/array_utils.cc -internal/ceres/array_utils.h -internal/ceres/blas.cc -internal/ceres/blas.h -internal/ceres/block_evaluate_preparer.cc -internal/ceres/block_evaluate_preparer.h -internal/ceres/block_jacobian_writer.cc -internal/ceres/block_jacobian_writer.h -internal/ceres/block_jacobi_preconditioner.cc -internal/ceres/block_jacobi_preconditioner.h -internal/ceres/block_random_access_dense_matrix.cc -internal/ceres/block_random_access_dense_matrix.h -internal/ceres/block_random_access_diagonal_matrix.cc -internal/ceres/block_random_access_diagonal_matrix.h -internal/ceres/block_random_access_matrix.cc -internal/ceres/block_random_access_matrix.h -internal/ceres/block_random_access_sparse_matrix.cc -internal/ceres/block_random_access_sparse_matrix.h -internal/ceres/block_sparse_matrix.cc -internal/ceres/block_sparse_matrix.h -internal/ceres/block_structure.cc -internal/ceres/block_structure.h -internal/ceres/callbacks.cc -internal/ceres/callbacks.h -internal/ceres/canonical_views_clustering.cc -internal/ceres/canonical_views_clustering.h -internal/ceres/c_api.cc -internal/ceres/casts.h -internal/ceres/cgnr_linear_operator.h -internal/ceres/cgnr_solver.cc -internal/ceres/cgnr_solver.h -internal/ceres/compressed_col_sparse_matrix_utils.cc -internal/ceres/compressed_col_sparse_matrix_utils.h -internal/ceres/compressed_row_jacobian_writer.cc -internal/ceres/compressed_row_jacobian_writer.h -internal/ceres/compressed_row_sparse_matrix.cc -internal/ceres/compressed_row_sparse_matrix.h -internal/ceres/concurrent_queue.h -internal/ceres/conditioned_cost_function.cc -internal/ceres/conjugate_gradients_solver.cc -internal/ceres/conjugate_gradients_solver.h -internal/ceres/context.cc -internal/ceres/context_impl.cc -internal/ceres/context_impl.h -internal/ceres/coordinate_descent_minimizer.cc -internal/ceres/coordinate_descent_minimizer.h -internal/ceres/corrector.cc -internal/ceres/corrector.h -internal/ceres/covariance.cc -internal/ceres/covariance_impl.cc -internal/ceres/covariance_impl.h -internal/ceres/cxsparse.cc -internal/ceres/cxsparse.h -internal/ceres/dense_jacobian_writer.h -internal/ceres/dense_normal_cholesky_solver.cc -internal/ceres/dense_normal_cholesky_solver.h -internal/ceres/dense_qr_solver.cc -internal/ceres/dense_qr_solver.h -internal/ceres/dense_sparse_matrix.cc -internal/ceres/dense_sparse_matrix.h -internal/ceres/detect_structure.cc -internal/ceres/detect_structure.h -internal/ceres/dogleg_strategy.cc -internal/ceres/dogleg_strategy.h -internal/ceres/dynamic_compressed_row_finalizer.h -internal/ceres/dynamic_compressed_row_jacobian_writer.cc -internal/ceres/dynamic_compressed_row_jacobian_writer.h -internal/ceres/dynamic_compressed_row_sparse_matrix.cc -internal/ceres/dynamic_compressed_row_sparse_matrix.h -internal/ceres/dynamic_sparse_normal_cholesky_solver.cc -internal/ceres/dynamic_sparse_normal_cholesky_solver.h -internal/ceres/eigensparse.cc -internal/ceres/eigensparse.h -internal/ceres/evaluator.cc -internal/ceres/evaluator.h -internal/ceres/execution_summary.h -internal/ceres/file.cc -internal/ceres/file.h -internal/ceres/float_cxsparse.cc -internal/ceres/float_cxsparse.h -internal/ceres/float_suitesparse.cc -internal/ceres/float_suitesparse.h -internal/ceres/function_sample.cc -internal/ceres/function_sample.h -internal/ceres/generated/partitioned_matrix_view_2_2_2.cc -internal/ceres/generated/partitioned_matrix_view_2_2_3.cc -internal/ceres/generated/partitioned_matrix_view_2_2_4.cc -internal/ceres/generated/partitioned_matrix_view_2_2_d.cc -internal/ceres/generated/partitioned_matrix_view_2_3_3.cc -internal/ceres/generated/partitioned_matrix_view_2_3_4.cc -internal/ceres/generated/partitioned_matrix_view_2_3_6.cc -internal/ceres/generated/partitioned_matrix_view_2_3_9.cc -internal/ceres/generated/partitioned_matrix_view_2_3_d.cc -internal/ceres/generated/partitioned_matrix_view_2_4_3.cc -internal/ceres/generated/partitioned_matrix_view_2_4_4.cc -internal/ceres/generated/partitioned_matrix_view_2_4_6.cc -internal/ceres/generated/partitioned_matrix_view_2_4_8.cc -internal/ceres/generated/partitioned_matrix_view_2_4_9.cc -internal/ceres/generated/partitioned_matrix_view_2_4_d.cc -internal/ceres/generated/partitioned_matrix_view_2_d_d.cc -internal/ceres/generated/partitioned_matrix_view_3_3_3.cc -internal/ceres/generated/partitioned_matrix_view_4_4_2.cc -internal/ceres/generated/partitioned_matrix_view_4_4_3.cc -internal/ceres/generated/partitioned_matrix_view_4_4_4.cc -internal/ceres/generated/partitioned_matrix_view_4_4_d.cc -internal/ceres/generated/partitioned_matrix_view_d_d_d.cc -internal/ceres/generated/schur_eliminator_2_2_2.cc -internal/ceres/generated/schur_eliminator_2_2_3.cc -internal/ceres/generated/schur_eliminator_2_2_4.cc -internal/ceres/generated/schur_eliminator_2_2_d.cc -internal/ceres/generated/schur_eliminator_2_3_3.cc -internal/ceres/generated/schur_eliminator_2_3_4.cc -internal/ceres/generated/schur_eliminator_2_3_6.cc -internal/ceres/generated/schur_eliminator_2_3_9.cc -internal/ceres/generated/schur_eliminator_2_3_d.cc -internal/ceres/generated/schur_eliminator_2_4_3.cc -internal/ceres/generated/schur_eliminator_2_4_4.cc -internal/ceres/generated/schur_eliminator_2_4_6.cc -internal/ceres/generated/schur_eliminator_2_4_8.cc -internal/ceres/generated/schur_eliminator_2_4_9.cc -internal/ceres/generated/schur_eliminator_2_4_d.cc -internal/ceres/generated/schur_eliminator_2_d_d.cc -internal/ceres/generated/schur_eliminator_3_3_3.cc -internal/ceres/generated/schur_eliminator_4_4_2.cc -internal/ceres/generated/schur_eliminator_4_4_3.cc -internal/ceres/generated/schur_eliminator_4_4_4.cc -internal/ceres/generated/schur_eliminator_4_4_d.cc -internal/ceres/generated/schur_eliminator_d_d_d.cc -internal/ceres/generate_template_specializations.py -internal/ceres/gradient_checker.cc -internal/ceres/gradient_checking_cost_function.cc -internal/ceres/gradient_checking_cost_function.h -internal/ceres/gradient_problem.cc -internal/ceres/gradient_problem_evaluator.h -internal/ceres/gradient_problem_solver.cc -internal/ceres/graph_algorithms.h -internal/ceres/graph.h -internal/ceres/implicit_schur_complement.cc -internal/ceres/implicit_schur_complement.h -internal/ceres/inner_product_computer.cc -internal/ceres/inner_product_computer.h -internal/ceres/invert_psd_matrix.h -internal/ceres/is_close.cc -internal/ceres/is_close.h -internal/ceres/iterative_refiner.cc -internal/ceres/iterative_refiner.h -internal/ceres/iterative_schur_complement_solver.cc -internal/ceres/iterative_schur_complement_solver.h -internal/ceres/lapack.cc -internal/ceres/lapack.h -internal/ceres/levenberg_marquardt_strategy.cc -internal/ceres/levenberg_marquardt_strategy.h -internal/ceres/linear_least_squares_problems.cc -internal/ceres/linear_least_squares_problems.h -internal/ceres/linear_operator.cc -internal/ceres/linear_operator.h -internal/ceres/linear_solver.cc -internal/ceres/linear_solver.h -internal/ceres/line_search.cc -internal/ceres/line_search_direction.cc -internal/ceres/line_search_direction.h -internal/ceres/line_search.h -internal/ceres/line_search_minimizer.cc -internal/ceres/line_search_minimizer.h -internal/ceres/line_search_preprocessor.cc -internal/ceres/line_search_preprocessor.h -internal/ceres/local_parameterization.cc -internal/ceres/loss_function.cc -internal/ceres/low_rank_inverse_hessian.cc -internal/ceres/low_rank_inverse_hessian.h -internal/ceres/map_util.h -internal/ceres/minimizer.cc -internal/ceres/minimizer.h -internal/ceres/normal_prior.cc -internal/ceres/pair_hash.h -internal/ceres/parallel_for_cxx.cc -internal/ceres/parallel_for.h -internal/ceres/parallel_for_nothreads.cc -internal/ceres/parallel_for_openmp.cc -internal/ceres/parallel_utils.cc -internal/ceres/parallel_utils.h -internal/ceres/parameter_block.h -internal/ceres/parameter_block_ordering.cc -internal/ceres/parameter_block_ordering.h -internal/ceres/partitioned_matrix_view.cc -internal/ceres/partitioned_matrix_view.h -internal/ceres/partitioned_matrix_view_impl.h -internal/ceres/partitioned_matrix_view_template.py -internal/ceres/polynomial.cc -internal/ceres/polynomial.h -internal/ceres/preconditioner.cc -internal/ceres/preconditioner.h -internal/ceres/preprocessor.cc -internal/ceres/preprocessor.h -internal/ceres/problem.cc -internal/ceres/problem_impl.cc -internal/ceres/problem_impl.h -internal/ceres/program.cc -internal/ceres/program_evaluator.h -internal/ceres/program.h -internal/ceres/random.h -internal/ceres/reorder_program.cc -internal/ceres/reorder_program.h -internal/ceres/residual_block.cc -internal/ceres/residual_block.h -internal/ceres/residual_block_utils.cc -internal/ceres/residual_block_utils.h -internal/ceres/schur_complement_solver.cc -internal/ceres/schur_complement_solver.h -internal/ceres/schur_eliminator.cc -internal/ceres/schur_eliminator.h -internal/ceres/schur_eliminator_impl.h -internal/ceres/schur_eliminator_template.py -internal/ceres/schur_jacobi_preconditioner.cc -internal/ceres/schur_jacobi_preconditioner.h -internal/ceres/schur_templates.cc -internal/ceres/schur_templates.h -internal/ceres/scoped_thread_token.h -internal/ceres/scratch_evaluate_preparer.cc -internal/ceres/scratch_evaluate_preparer.h -internal/ceres/single_linkage_clustering.cc -internal/ceres/single_linkage_clustering.h -internal/ceres/small_blas_generic.h -internal/ceres/small_blas.h -internal/ceres/solver.cc -internal/ceres/solver_utils.cc -internal/ceres/solver_utils.h -internal/ceres/sparse_cholesky.cc -internal/ceres/sparse_cholesky.h -internal/ceres/sparse_matrix.cc -internal/ceres/sparse_matrix.h -internal/ceres/sparse_normal_cholesky_solver.cc -internal/ceres/sparse_normal_cholesky_solver.h -internal/ceres/split.cc -internal/ceres/split.h -internal/ceres/stl_util.h -internal/ceres/stringprintf.cc -internal/ceres/stringprintf.h -internal/ceres/subset_preconditioner.cc -internal/ceres/subset_preconditioner.h -internal/ceres/suitesparse.cc -internal/ceres/suitesparse.h -internal/ceres/thread_pool.cc -internal/ceres/thread_pool.h -internal/ceres/thread_token_provider.cc -internal/ceres/thread_token_provider.h -internal/ceres/triplet_sparse_matrix.cc -internal/ceres/triplet_sparse_matrix.h -internal/ceres/trust_region_minimizer.cc -internal/ceres/trust_region_minimizer.h -internal/ceres/trust_region_preprocessor.cc -internal/ceres/trust_region_preprocessor.h -internal/ceres/trust_region_step_evaluator.cc -internal/ceres/trust_region_step_evaluator.h -internal/ceres/trust_region_strategy.cc -internal/ceres/trust_region_strategy.h -internal/ceres/types.cc -internal/ceres/visibility_based_preconditioner.cc -internal/ceres/visibility_based_preconditioner.h -internal/ceres/visibility.cc -internal/ceres/visibility.h -internal/ceres/wall_time.cc -internal/ceres/wall_time.h -config/ceres/internal/config.h diff --git a/extern/ceres/include/ceres/autodiff_cost_function.h b/extern/ceres/include/ceres/autodiff_cost_function.h index 207f0a41688..cd256432a98 100644 --- a/extern/ceres/include/ceres/autodiff_cost_function.h +++ b/extern/ceres/include/ceres/autodiff_cost_function.h @@ -151,7 +151,8 @@ namespace ceres { template <typename CostFunctor, int kNumResiduals, // Number of residuals, or ceres::DYNAMIC. int... Ns> // Number of parameters in each parameter block. -class AutoDiffCostFunction : public SizedCostFunction<kNumResiduals, Ns...> { +class AutoDiffCostFunction final + : public SizedCostFunction<kNumResiduals, Ns...> { public: // Takes ownership of functor by default. Uses the template-provided // value for the number of residuals ("kNumResiduals"). @@ -178,7 +179,7 @@ class AutoDiffCostFunction : public SizedCostFunction<kNumResiduals, Ns...> { SizedCostFunction<kNumResiduals, Ns...>::set_num_residuals(num_residuals); } - explicit AutoDiffCostFunction(AutoDiffCostFunction&& other) + AutoDiffCostFunction(AutoDiffCostFunction&& other) : functor_(std::move(other.functor_)), ownership_(other.ownership_) {} virtual ~AutoDiffCostFunction() { @@ -215,6 +216,8 @@ class AutoDiffCostFunction : public SizedCostFunction<kNumResiduals, Ns...> { jacobians); }; + const CostFunctor& functor() const { return *functor_; } + private: std::unique_ptr<CostFunctor> functor_; Ownership ownership_; diff --git a/extern/ceres/include/ceres/autodiff_first_order_function.h b/extern/ceres/include/ceres/autodiff_first_order_function.h index b98d845655b..7c13f4239a6 100644 --- a/extern/ceres/include/ceres/autodiff_first_order_function.h +++ b/extern/ceres/include/ceres/autodiff_first_order_function.h @@ -102,7 +102,7 @@ namespace ceres { // seen where instead of using a_ directly, a_ is wrapped with T(a_). template <typename FirstOrderFunctor, int kNumParameters> -class AutoDiffFirstOrderFunction : public FirstOrderFunction { +class AutoDiffFirstOrderFunction final : public FirstOrderFunction { public: // Takes ownership of functor. explicit AutoDiffFirstOrderFunction(FirstOrderFunctor* functor) @@ -110,8 +110,6 @@ class AutoDiffFirstOrderFunction : public FirstOrderFunction { static_assert(kNumParameters > 0, "kNumParameters must be positive"); } - virtual ~AutoDiffFirstOrderFunction() {} - bool Evaluate(const double* const parameters, double* cost, double* gradient) const override { @@ -119,7 +117,7 @@ class AutoDiffFirstOrderFunction : public FirstOrderFunction { return (*functor_)(parameters, cost); } - typedef Jet<double, kNumParameters> JetT; + using JetT = Jet<double, kNumParameters>; internal::FixedArray<JetT, (256 * 7) / sizeof(JetT)> x(kNumParameters); for (int i = 0; i < kNumParameters; ++i) { x[i].a = parameters[i]; @@ -142,6 +140,8 @@ class AutoDiffFirstOrderFunction : public FirstOrderFunction { int NumParameters() const override { return kNumParameters; } + const FirstOrderFunctor& functor() const { return *functor_; } + private: std::unique_ptr<FirstOrderFunctor> functor_; }; diff --git a/extern/ceres/include/ceres/autodiff_local_parameterization.h b/extern/ceres/include/ceres/autodiff_local_parameterization.h index d694376fdd1..5f9b04d0670 100644 --- a/extern/ceres/include/ceres/autodiff_local_parameterization.h +++ b/extern/ceres/include/ceres/autodiff_local_parameterization.h @@ -40,6 +40,10 @@ namespace ceres { +// WARNING: LocalParameterizations are deprecated, so is +// AutoDiffLocalParameterization. They will be removed from Ceres Solver in +// version 2.2.0. Please use Manifolds and AutoDiffManifold instead. + // Create local parameterization with Jacobians computed via automatic // differentiation. For more information on local parameterizations, // see include/ceres/local_parameterization.h @@ -106,7 +110,8 @@ namespace ceres { // seen where instead of using k_ directly, k_ is wrapped with T(k_). template <typename Functor, int kGlobalSize, int kLocalSize> -class AutoDiffLocalParameterization : public LocalParameterization { +class CERES_DEPRECATED_WITH_MSG("Use AutoDiffManifold instead.") + AutoDiffLocalParameterization : public LocalParameterization { public: AutoDiffLocalParameterization() : functor_(new Functor()) {} @@ -114,7 +119,6 @@ class AutoDiffLocalParameterization : public LocalParameterization { explicit AutoDiffLocalParameterization(Functor* functor) : functor_(functor) {} - virtual ~AutoDiffLocalParameterization() {} bool Plus(const double* x, const double* delta, double* x_plus_delta) const override { @@ -133,7 +137,7 @@ class AutoDiffLocalParameterization : public LocalParameterization { } const double* parameter_ptrs[2] = {x, zero_delta}; - double* jacobian_ptrs[2] = {NULL, jacobian}; + double* jacobian_ptrs[2] = {nullptr, jacobian}; return internal::AutoDifferentiate< kGlobalSize, internal::StaticParameterDims<kGlobalSize, kLocalSize>>( @@ -143,6 +147,8 @@ class AutoDiffLocalParameterization : public LocalParameterization { int GlobalSize() const override { return kGlobalSize; } int LocalSize() const override { return kLocalSize; } + const Functor& functor() const { return *functor_; } + private: std::unique_ptr<Functor> functor_; }; diff --git a/extern/ceres/include/ceres/autodiff_manifold.h b/extern/ceres/include/ceres/autodiff_manifold.h new file mode 100644 index 00000000000..3063e19e802 --- /dev/null +++ b/extern/ceres/include/ceres/autodiff_manifold.h @@ -0,0 +1,259 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#ifndef CERES_PUBLIC_AUTODIFF_MANIFOLD_H_ +#define CERES_PUBLIC_AUTODIFF_MANIFOLD_H_ + +#include <memory> + +#include "ceres/internal/autodiff.h" +#include "ceres/manifold.h" + +namespace ceres { + +// Create a Manifold with Jacobians computed via automatic differentiation. For +// more information on manifolds, see include/ceres/manifold.h +// +// To get an auto differentiated manifold, you must define a class/struct with +// templated Plus and Minus functions that compute +// +// x_plus_delta = Plus(x, delta); +// y_minus_x = Minus(y, x); +// +// Where, x, y and x_plus_y are vectors on the manifold in the ambient space (so +// they are kAmbientSize vectors) and delta, y_minus_x are vectors in the +// tangent space (so they are kTangentSize vectors). +// +// The Functor should have the signature: +// +// struct Functor { +// template <typename T> +// bool Plus(const T* x, const T* delta, T* x_plus_delta) const; +// +// template <typename T> +// bool Minus(const T* y, const T* x, T* y_minus_x) const; +// }; +// +// Observe that the Plus and Minus operations are templated on the parameter T. +// The autodiff framework substitutes appropriate "Jet" objects for T in order +// to compute the derivative when necessary. This is the same mechanism that is +// used to compute derivatives when using AutoDiffCostFunction. +// +// Plus and Minus should return true if the computation is successful and false +// otherwise, in which case the result will not be used. +// +// Given this Functor, the corresponding Manifold can be constructed as: +// +// AutoDiffManifold<Functor, kAmbientSize, kTangentSize> manifold; +// +// As a concrete example consider the case of Quaternions. Quaternions form a +// three dimensional manifold embedded in R^4, i.e. they have an ambient +// dimension of 4 and their tangent space has dimension 3. The following Functor +// (taken from autodiff_manifold_test.cc) defines the Plus and Minus operations +// on the Quaternion manifold: +// +// NOTE: The following is only used for illustration purposes. Ceres Solver +// ships with optimized production grade QuaternionManifold implementation. See +// manifold.h. +// +// This functor assumes that the quaternions are laid out as [w,x,y,z] in +// memory, i.e. the real or scalar part is the first coordinate. +// +// struct QuaternionFunctor { +// template <typename T> +// bool Plus(const T* x, const T* delta, T* x_plus_delta) const { +// const T squared_norm_delta = +// delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]; +// +// T q_delta[4]; +// if (squared_norm_delta > T(0.0)) { +// T norm_delta = sqrt(squared_norm_delta); +// const T sin_delta_by_delta = sin(norm_delta) / norm_delta; +// q_delta[0] = cos(norm_delta); +// q_delta[1] = sin_delta_by_delta * delta[0]; +// q_delta[2] = sin_delta_by_delta * delta[1]; +// q_delta[3] = sin_delta_by_delta * delta[2]; +// } else { +// // We do not just use q_delta = [1,0,0,0] here because that is a +// // constant and when used for automatic differentiation will +// // lead to a zero derivative. Instead we take a first order +// // approximation and evaluate it at zero. +// q_delta[0] = T(1.0); +// q_delta[1] = delta[0]; +// q_delta[2] = delta[1]; +// q_delta[3] = delta[2]; +// } +// +// QuaternionProduct(q_delta, x, x_plus_delta); +// return true; +// } +// +// template <typename T> +// bool Minus(const T* y, const T* x, T* y_minus_x) const { +// T minus_x[4] = {x[0], -x[1], -x[2], -x[3]}; +// T ambient_y_minus_x[4]; +// QuaternionProduct(y, minus_x, ambient_y_minus_x); +// T u_norm = sqrt(ambient_y_minus_x[1] * ambient_y_minus_x[1] + +// ambient_y_minus_x[2] * ambient_y_minus_x[2] + +// ambient_y_minus_x[3] * ambient_y_minus_x[3]); +// if (u_norm > 0.0) { +// T theta = atan2(u_norm, ambient_y_minus_x[0]); +// y_minus_x[0] = theta * ambient_y_minus_x[1] / u_norm; +// y_minus_x[1] = theta * ambient_y_minus_x[2] / u_norm; +// y_minus_x[2] = theta * ambient_y_minus_x[3] / u_norm; +// } else { +// // We do not use [0,0,0] here because even though the value part is +// // a constant, the derivative part is not. +// y_minus_x[0] = ambient_y_minus_x[1]; +// y_minus_x[1] = ambient_y_minus_x[2]; +// y_minus_x[2] = ambient_y_minus_x[3]; +// } +// return true; +// } +// }; +// +// Then given this struct, the auto differentiated Quaternion Manifold can now +// be constructed as +// +// Manifold* manifold = new AutoDiffManifold<QuaternionFunctor, 4, 3>; + +template <typename Functor, int kAmbientSize, int kTangentSize> +class AutoDiffManifold final : public Manifold { + public: + AutoDiffManifold() : functor_(std::make_unique<Functor>()) {} + + // Takes ownership of functor. + explicit AutoDiffManifold(Functor* functor) : functor_(functor) {} + + int AmbientSize() const override { return kAmbientSize; } + int TangentSize() const override { return kTangentSize; } + + bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const override { + return functor_->Plus(x, delta, x_plus_delta); + } + + bool PlusJacobian(const double* x, double* jacobian) const override; + + bool Minus(const double* y, + const double* x, + double* y_minus_x) const override { + return functor_->Minus(y, x, y_minus_x); + } + + bool MinusJacobian(const double* x, double* jacobian) const override; + + const Functor& functor() const { return *functor_; } + + private: + std::unique_ptr<Functor> functor_; +}; + +namespace internal { + +// The following two helper structs are needed to interface the Plus and Minus +// methods of the ManifoldFunctor with the automatic differentiation which +// expects a Functor with operator(). +template <typename Functor> +struct PlusWrapper { + explicit PlusWrapper(const Functor& functor) : functor(functor) {} + template <typename T> + bool operator()(const T* x, const T* delta, T* x_plus_delta) const { + return functor.Plus(x, delta, x_plus_delta); + } + const Functor& functor; +}; + +template <typename Functor> +struct MinusWrapper { + explicit MinusWrapper(const Functor& functor) : functor(functor) {} + template <typename T> + bool operator()(const T* y, const T* x, T* y_minus_x) const { + return functor.Minus(y, x, y_minus_x); + } + const Functor& functor; +}; +} // namespace internal + +template <typename Functor, int kAmbientSize, int kTangentSize> +bool AutoDiffManifold<Functor, kAmbientSize, kTangentSize>::PlusJacobian( + const double* x, double* jacobian) const { + double zero_delta[kTangentSize]; + for (int i = 0; i < kTangentSize; ++i) { + zero_delta[i] = 0.0; + } + + double x_plus_delta[kAmbientSize]; + for (int i = 0; i < kAmbientSize; ++i) { + x_plus_delta[i] = 0.0; + } + + const double* parameter_ptrs[2] = {x, zero_delta}; + + // PlusJacobian is D_2 Plus(x,0) so we only need to compute the Jacobian + // w.r.t. the second argument. + double* jacobian_ptrs[2] = {nullptr, jacobian}; + return internal::AutoDifferentiate< + kAmbientSize, + internal::StaticParameterDims<kAmbientSize, kTangentSize>>( + internal::PlusWrapper<Functor>(*functor_), + parameter_ptrs, + kAmbientSize, + x_plus_delta, + jacobian_ptrs); +} + +template <typename Functor, int kAmbientSize, int kTangentSize> +bool AutoDiffManifold<Functor, kAmbientSize, kTangentSize>::MinusJacobian( + const double* x, double* jacobian) const { + double y_minus_x[kTangentSize]; + for (int i = 0; i < kTangentSize; ++i) { + y_minus_x[i] = 0.0; + } + + const double* parameter_ptrs[2] = {x, x}; + + // MinusJacobian is D_1 Minus(x,x), so we only need to compute the Jacobian + // w.r.t. the first argument. + double* jacobian_ptrs[2] = {jacobian, nullptr}; + return internal::AutoDifferentiate< + kTangentSize, + internal::StaticParameterDims<kAmbientSize, kAmbientSize>>( + internal::MinusWrapper<Functor>(*functor_), + parameter_ptrs, + kTangentSize, + y_minus_x, + jacobian_ptrs); +} + +} // namespace ceres + +#endif // CERES_PUBLIC_AUTODIFF_MANIFOLD_H_ diff --git a/extern/ceres/include/ceres/c_api.h b/extern/ceres/include/ceres/c_api.h index 91b82bf995f..1be8ca2e077 100644 --- a/extern/ceres/include/ceres/c_api.h +++ b/extern/ceres/include/ceres/c_api.h @@ -39,7 +39,7 @@ #define CERES_PUBLIC_C_API_H_ // clang-format off -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/internal/disable_warnings.h" // clang-format on diff --git a/extern/ceres/include/ceres/ceres.h b/extern/ceres/include/ceres/ceres.h index d249351694c..c32477d4254 100644 --- a/extern/ceres/include/ceres/ceres.h +++ b/extern/ceres/include/ceres/ceres.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2019 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -35,7 +35,9 @@ #define CERES_PUBLIC_CERES_H_ #include "ceres/autodiff_cost_function.h" +#include "ceres/autodiff_first_order_function.h" #include "ceres/autodiff_local_parameterization.h" +#include "ceres/autodiff_manifold.h" #include "ceres/conditioned_cost_function.h" #include "ceres/context.h" #include "ceres/cost_function.h" @@ -47,19 +49,25 @@ #include "ceres/dynamic_cost_function_to_functor.h" #include "ceres/dynamic_numeric_diff_cost_function.h" #include "ceres/evaluation_callback.h" +#include "ceres/first_order_function.h" #include "ceres/gradient_checker.h" #include "ceres/gradient_problem.h" #include "ceres/gradient_problem_solver.h" #include "ceres/iteration_callback.h" #include "ceres/jet.h" +#include "ceres/line_manifold.h" #include "ceres/local_parameterization.h" #include "ceres/loss_function.h" +#include "ceres/manifold.h" #include "ceres/numeric_diff_cost_function.h" +#include "ceres/numeric_diff_first_order_function.h" #include "ceres/numeric_diff_options.h" #include "ceres/ordered_groups.h" #include "ceres/problem.h" +#include "ceres/product_manifold.h" #include "ceres/sized_cost_function.h" #include "ceres/solver.h" +#include "ceres/sphere_manifold.h" #include "ceres/types.h" #include "ceres/version.h" diff --git a/extern/ceres/include/ceres/conditioned_cost_function.h b/extern/ceres/include/ceres/conditioned_cost_function.h index a57ee209b80..e4c3decbfd5 100644 --- a/extern/ceres/include/ceres/conditioned_cost_function.h +++ b/extern/ceres/include/ceres/conditioned_cost_function.h @@ -71,18 +71,18 @@ namespace ceres { // ccf_residual[i] = f_i(my_cost_function_residual[i]) // // and the Jacobian will be affected appropriately. -class CERES_EXPORT ConditionedCostFunction : public CostFunction { +class CERES_EXPORT ConditionedCostFunction final : public CostFunction { public: // Builds a cost function based on a wrapped cost function, and a // per-residual conditioner. Takes ownership of all of the wrapped cost // functions, or not, depending on the ownership parameter. Conditioners - // may be NULL, in which case the corresponding residual is not modified. + // may be nullptr, in which case the corresponding residual is not modified. // // The conditioners can repeat. ConditionedCostFunction(CostFunction* wrapped_cost_function, const std::vector<CostFunction*>& conditioners, Ownership ownership); - virtual ~ConditionedCostFunction(); + ~ConditionedCostFunction() override; bool Evaluate(double const* const* parameters, double* residuals, diff --git a/extern/ceres/include/ceres/context.h b/extern/ceres/include/ceres/context.h index d08e32b31a8..6c6e8f4c953 100644 --- a/extern/ceres/include/ceres/context.h +++ b/extern/ceres/include/ceres/context.h @@ -31,6 +31,8 @@ #ifndef CERES_PUBLIC_CONTEXT_H_ #define CERES_PUBLIC_CONTEXT_H_ +#include "ceres/internal/export.h" + namespace ceres { // A global context for processing data in Ceres. This provides a mechanism to @@ -39,13 +41,13 @@ namespace ceres { // Problems, either serially or in parallel. When using it with multiple // Problems at the same time, they may end up contending for resources // (e.g. threads) managed by the Context. -class Context { +class CERES_EXPORT Context { public: - Context() {} + Context(); Context(const Context&) = delete; void operator=(const Context&) = delete; - virtual ~Context() {} + virtual ~Context(); // Creates a context object and the caller takes ownership. static Context* Create(); diff --git a/extern/ceres/include/ceres/cost_function.h b/extern/ceres/include/ceres/cost_function.h index d1550c119e8..fef972b75af 100644 --- a/extern/ceres/include/ceres/cost_function.h +++ b/extern/ceres/include/ceres/cost_function.h @@ -48,7 +48,7 @@ #include <vector> #include "ceres/internal/disable_warnings.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { @@ -63,11 +63,11 @@ namespace ceres { // when added with AddResidualBlock(). class CERES_EXPORT CostFunction { public: - CostFunction() : num_residuals_(0) {} + CostFunction(); CostFunction(const CostFunction&) = delete; void operator=(const CostFunction&) = delete; - virtual ~CostFunction() {} + virtual ~CostFunction(); // Inputs: // @@ -92,8 +92,8 @@ class CERES_EXPORT CostFunction { // jacobians[i][r*parameter_block_size_[i] + c] = // d residual[r] / d parameters[i][c] // - // If jacobians is NULL, then no derivatives are returned; this is - // the case when computing cost only. If jacobians[i] is NULL, then + // If jacobians is nullptr, then no derivatives are returned; this is + // the case when computing cost only. If jacobians[i] is nullptr, then // the jacobian block corresponding to the i'th parameter block must // not to be returned. // diff --git a/extern/ceres/include/ceres/cost_function_to_functor.h b/extern/ceres/include/ceres/cost_function_to_functor.h index 9364293afc5..08a8050c5f8 100644 --- a/extern/ceres/include/ceres/cost_function_to_functor.h +++ b/extern/ceres/include/ceres/cost_function_to_functor.h @@ -94,10 +94,11 @@ #include "ceres/cost_function.h" #include "ceres/dynamic_cost_function_to_functor.h" +#include "ceres/internal/export.h" #include "ceres/internal/fixed_array.h" #include "ceres/internal/parameter_dims.h" -#include "ceres/internal/port.h" #include "ceres/types.h" +#include "glog/logging.h" namespace ceres { diff --git a/extern/ceres/include/ceres/covariance.h b/extern/ceres/include/ceres/covariance.h index 2fe025df3ce..60bcc80b80f 100644 --- a/extern/ceres/include/ceres/covariance.h +++ b/extern/ceres/include/ceres/covariance.h @@ -35,8 +35,9 @@ #include <utility> #include <vector> +#include "ceres/internal/config.h" #include "ceres/internal/disable_warnings.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/types.h" namespace ceres { @@ -145,7 +146,7 @@ class CovarianceImpl; // a. The rank deficiency arises from overparameterization. e.g., a // four dimensional quaternion used to parameterize SO(3), which is // a three dimensional manifold. In cases like this, the user should -// use an appropriate LocalParameterization. Not only will this lead +// use an appropriate LocalParameterization/Manifold. Not only will this lead // to better numerical behaviour of the Solver, it will also expose // the rank deficiency to the Covariance object so that it can // handle it correctly. diff --git a/extern/ceres/include/ceres/crs_matrix.h b/extern/ceres/include/ceres/crs_matrix.h index bc618fa0905..faa0f988528 100644 --- a/extern/ceres/include/ceres/crs_matrix.h +++ b/extern/ceres/include/ceres/crs_matrix.h @@ -34,17 +34,17 @@ #include <vector> #include "ceres/internal/disable_warnings.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { // A compressed row sparse matrix used primarily for communicating the // Jacobian matrix to the user. struct CERES_EXPORT CRSMatrix { - CRSMatrix() : num_rows(0), num_cols(0) {} + CRSMatrix() = default; - int num_rows; - int num_cols; + int num_rows{0}; + int num_cols{0}; // A compressed row matrix stores its contents in three arrays, // rows, cols and values. diff --git a/extern/ceres/include/ceres/cubic_interpolation.h b/extern/ceres/include/ceres/cubic_interpolation.h index 9b9ea4a942c..3ca6b11b407 100644 --- a/extern/ceres/include/ceres/cubic_interpolation.h +++ b/extern/ceres/include/ceres/cubic_interpolation.h @@ -32,7 +32,7 @@ #define CERES_PUBLIC_CUBIC_INTERPOLATION_H_ #include "Eigen/Core" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "glog/logging.h" namespace ceres { @@ -59,8 +59,8 @@ namespace ceres { // http://en.wikipedia.org/wiki/Cubic_Hermite_spline // http://en.wikipedia.org/wiki/Bicubic_interpolation // -// f if not NULL will contain the interpolated function values. -// dfdx if not NULL will contain the interpolated derivative values. +// f if not nullptr will contain the interpolated function values. +// dfdx if not nullptr will contain the interpolated derivative values. template <int kDataDimension> void CubicHermiteSpline(const Eigen::Matrix<double, kDataDimension, 1>& p0, const Eigen::Matrix<double, kDataDimension, 1>& p1, @@ -69,7 +69,7 @@ void CubicHermiteSpline(const Eigen::Matrix<double, kDataDimension, 1>& p0, const double x, double* f, double* dfdx) { - typedef Eigen::Matrix<double, kDataDimension, 1> VType; + using VType = Eigen::Matrix<double, kDataDimension, 1>; const VType a = 0.5 * (-p0 + 3.0 * p1 - 3.0 * p2 + p3); const VType b = 0.5 * (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3); const VType c = 0.5 * (-p0 + p2); @@ -79,12 +79,12 @@ void CubicHermiteSpline(const Eigen::Matrix<double, kDataDimension, 1>& p0, // derivative. // f = ax^3 + bx^2 + cx + d - if (f != NULL) { + if (f != nullptr) { Eigen::Map<VType>(f, kDataDimension) = d + x * (c + x * (b + x * a)); } // dfdx = 3ax^2 + 2bx + c - if (dfdx != NULL) { + if (dfdx != nullptr) { Eigen::Map<VType>(dfdx, kDataDimension) = c + x * (2.0 * b + 3.0 * a * x); } } @@ -143,7 +143,7 @@ class CubicInterpolator { // The following two Evaluate overloads are needed for interfacing // with automatic differentiation. The first is for when a scalar // evaluation is done, and the second one is for when Jets are used. - void Evaluate(const double& x, double* f) const { Evaluate(x, f, NULL); } + void Evaluate(const double& x, double* f) const { Evaluate(x, f, nullptr); } template <typename JetT> void Evaluate(const JetT& x, JetT* f) const { @@ -191,7 +191,7 @@ struct Grid1D { } EIGEN_STRONG_INLINE void GetValue(const int n, double* f) const { - const int idx = std::min(std::max(begin_, n), end_ - 1) - begin_; + const int idx = (std::min)((std::max)(begin_, n), end_ - 1) - begin_; if (kInterleaved) { for (int i = 0; i < kDataDimension; ++i) { f[i] = static_cast<double>(data_[kDataDimension * idx + i]); @@ -317,10 +317,10 @@ class BiCubicInterpolator { // Interpolate vertically the interpolated value from each row and // compute the derivative along the columns. CubicHermiteSpline<Grid::DATA_DIMENSION>(f0, f1, f2, f3, r - row, f, dfdr); - if (dfdc != NULL) { + if (dfdc != nullptr) { // Interpolate vertically the derivative along the columns. CubicHermiteSpline<Grid::DATA_DIMENSION>( - df0dc, df1dc, df2dc, df3dc, r - row, dfdc, NULL); + df0dc, df1dc, df2dc, df3dc, r - row, dfdc, nullptr); } } @@ -328,7 +328,7 @@ class BiCubicInterpolator { // with automatic differentiation. The first is for when a scalar // evaluation is done, and the second one is for when Jets are used. void Evaluate(const double& r, const double& c, double* f) const { - Evaluate(r, c, f, NULL, NULL); + Evaluate(r, c, f, nullptr, nullptr); } template <typename JetT> @@ -402,9 +402,9 @@ struct Grid2D { EIGEN_STRONG_INLINE void GetValue(const int r, const int c, double* f) const { const int row_idx = - std::min(std::max(row_begin_, r), row_end_ - 1) - row_begin_; + (std::min)((std::max)(row_begin_, r), row_end_ - 1) - row_begin_; const int col_idx = - std::min(std::max(col_begin_, c), col_end_ - 1) - col_begin_; + (std::min)((std::max)(col_begin_, c), col_end_ - 1) - col_begin_; const int n = (kRowMajor) ? num_cols_ * row_idx + col_idx : num_rows_ * col_idx + row_idx; diff --git a/extern/ceres/include/ceres/dynamic_autodiff_cost_function.h b/extern/ceres/include/ceres/dynamic_autodiff_cost_function.h index 7ccf6a88c32..c21d0517f27 100644 --- a/extern/ceres/include/ceres/dynamic_autodiff_cost_function.h +++ b/extern/ceres/include/ceres/dynamic_autodiff_cost_function.h @@ -77,17 +77,17 @@ namespace ceres { // pass. There is a tradeoff with the size of the passes; you may want // to experiment with the stride. template <typename CostFunctor, int Stride = 4> -class DynamicAutoDiffCostFunction : public DynamicCostFunction { +class DynamicAutoDiffCostFunction final : public DynamicCostFunction { public: // Takes ownership by default. - DynamicAutoDiffCostFunction(CostFunctor* functor, - Ownership ownership = TAKE_OWNERSHIP) + explicit DynamicAutoDiffCostFunction(CostFunctor* functor, + Ownership ownership = TAKE_OWNERSHIP) : functor_(functor), ownership_(ownership) {} - explicit DynamicAutoDiffCostFunction(DynamicAutoDiffCostFunction&& other) + DynamicAutoDiffCostFunction(DynamicAutoDiffCostFunction&& other) : functor_(std::move(other.functor_)), ownership_(other.ownership_) {} - virtual ~DynamicAutoDiffCostFunction() { + ~DynamicAutoDiffCostFunction() override { // Manually release pointer if configured to not take ownership // rather than deleting only if ownership is taken. This is to // stay maximally compatible to old user code which may have @@ -105,7 +105,7 @@ class DynamicAutoDiffCostFunction : public DynamicCostFunction { << "You must call DynamicAutoDiffCostFunction::SetNumResiduals() " << "before DynamicAutoDiffCostFunction::Evaluate()."; - if (jacobians == NULL) { + if (jacobians == nullptr) { return (*functor_)(parameters, residuals); } @@ -150,7 +150,7 @@ class DynamicAutoDiffCostFunction : public DynamicCostFunction { jet_parameters[i] = &input_jets[parameter_cursor]; const int parameter_block_size = parameter_block_sizes()[i]; - if (jacobians[i] != NULL) { + if (jacobians[i] != nullptr) { if (!in_derivative_section) { start_derivative_section.push_back(parameter_cursor); in_derivative_section = true; @@ -209,7 +209,7 @@ class DynamicAutoDiffCostFunction : public DynamicCostFunction { parameter_cursor >= (start_derivative_section[current_derivative_section] + current_derivative_section_cursor)) { - if (jacobians[i] != NULL) { + if (jacobians[i] != nullptr) { input_jets[parameter_cursor].v[active_parameter_count] = 1.0; ++active_parameter_count; ++current_derivative_section_cursor; @@ -238,7 +238,7 @@ class DynamicAutoDiffCostFunction : public DynamicCostFunction { parameter_cursor >= (start_derivative_section[current_derivative_section] + current_derivative_section_cursor)) { - if (jacobians[i] != NULL) { + if (jacobians[i] != nullptr) { for (int k = 0; k < num_residuals(); ++k) { jacobians[i][k * parameter_block_sizes()[i] + j] = output_jets[k].v[active_parameter_count]; diff --git a/extern/ceres/include/ceres/dynamic_cost_function.h b/extern/ceres/include/ceres/dynamic_cost_function.h index 6e8a076ecd0..c84a366dafb 100644 --- a/extern/ceres/include/ceres/dynamic_cost_function.h +++ b/extern/ceres/include/ceres/dynamic_cost_function.h @@ -32,6 +32,7 @@ #define CERES_PUBLIC_DYNAMIC_COST_FUNCTION_H_ #include "ceres/cost_function.h" +#include "ceres/internal/disable_warnings.h" namespace ceres { @@ -40,8 +41,6 @@ namespace ceres { // parameter blocks and set the number of residuals at run time. class CERES_EXPORT DynamicCostFunction : public CostFunction { public: - ~DynamicCostFunction() {} - virtual void AddParameterBlock(int size) { mutable_parameter_block_sizes()->push_back(size); } @@ -53,4 +52,6 @@ class CERES_EXPORT DynamicCostFunction : public CostFunction { } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_PUBLIC_DYNAMIC_COST_FUNCTION_H_ diff --git a/extern/ceres/include/ceres/dynamic_cost_function_to_functor.h b/extern/ceres/include/ceres/dynamic_cost_function_to_functor.h index 8d174d8ecc2..5b5feaaf58e 100644 --- a/extern/ceres/include/ceres/dynamic_cost_function_to_functor.h +++ b/extern/ceres/include/ceres/dynamic_cost_function_to_functor.h @@ -37,8 +37,10 @@ #include <vector> #include "ceres/dynamic_cost_function.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/internal/fixed_array.h" -#include "ceres/internal/port.h" +#include "glog/logging.h" namespace ceres { @@ -100,7 +102,7 @@ namespace ceres { // private: // DynamicCostFunctionToFunctor intrinsic_projection_; // }; -class DynamicCostFunctionToFunctor { +class CERES_EXPORT DynamicCostFunctionToFunctor { public: // Takes ownership of cost_function. explicit DynamicCostFunctionToFunctor(CostFunction* cost_function) @@ -109,7 +111,7 @@ class DynamicCostFunctionToFunctor { } bool operator()(double const* const* parameters, double* residuals) const { - return cost_function_->Evaluate(parameters, residuals, NULL); + return cost_function_->Evaluate(parameters, residuals, nullptr); } template <typename JetT> @@ -187,4 +189,6 @@ class DynamicCostFunctionToFunctor { } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_PUBLIC_DYNAMIC_COST_FUNCTION_TO_FUNCTOR_H_ diff --git a/extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h b/extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h index ccc8f66db43..e1892e8ba4a 100644 --- a/extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h +++ b/extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h @@ -77,7 +77,7 @@ namespace ceres { // cost_function.AddParameterBlock(10); // cost_function.SetNumResiduals(21); template <typename CostFunctor, NumericDiffMethodType method = CENTRAL> -class DynamicNumericDiffCostFunction : public DynamicCostFunction { +class DynamicNumericDiffCostFunction final : public DynamicCostFunction { public: explicit DynamicNumericDiffCostFunction( const CostFunctor* functor, @@ -85,11 +85,10 @@ class DynamicNumericDiffCostFunction : public DynamicCostFunction { const NumericDiffOptions& options = NumericDiffOptions()) : functor_(functor), ownership_(ownership), options_(options) {} - explicit DynamicNumericDiffCostFunction( - DynamicNumericDiffCostFunction&& other) + DynamicNumericDiffCostFunction(DynamicNumericDiffCostFunction&& other) : functor_(std::move(other.functor_)), ownership_(other.ownership_) {} - virtual ~DynamicNumericDiffCostFunction() { + ~DynamicNumericDiffCostFunction() override { if (ownership_ != TAKE_OWNERSHIP) { functor_.release(); } @@ -111,7 +110,7 @@ class DynamicNumericDiffCostFunction : public DynamicCostFunction { const bool status = internal::VariadicEvaluate<internal::DynamicParameterDims>( *functor_.get(), parameters, residuals); - if (jacobians == NULL || !status) { + if (jacobians == nullptr || !status) { return status; } @@ -133,7 +132,7 @@ class DynamicNumericDiffCostFunction : public DynamicCostFunction { } for (size_t block = 0; block < block_sizes.size(); ++block) { - if (jacobians[block] != NULL && + if (jacobians[block] != nullptr && !NumericDiff<CostFunctor, method, ceres::DYNAMIC, diff --git a/extern/ceres/include/ceres/evaluation_callback.h b/extern/ceres/include/ceres/evaluation_callback.h index b9f5bbb5194..495d565047a 100644 --- a/extern/ceres/include/ceres/evaluation_callback.h +++ b/extern/ceres/include/ceres/evaluation_callback.h @@ -31,7 +31,7 @@ #ifndef CERES_PUBLIC_EVALUATION_CALLBACK_H_ #define CERES_PUBLIC_EVALUATION_CALLBACK_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { @@ -62,7 +62,7 @@ namespace ceres { // execute faster. class CERES_EXPORT EvaluationCallback { public: - virtual ~EvaluationCallback() {} + virtual ~EvaluationCallback(); // Called before Ceres requests residuals or jacobians for a given setting of // the parameters. User parameters (the double* values provided to the cost diff --git a/extern/ceres/include/ceres/first_order_function.h b/extern/ceres/include/ceres/first_order_function.h index 1420153b2aa..d718b6679ce 100644 --- a/extern/ceres/include/ceres/first_order_function.h +++ b/extern/ceres/include/ceres/first_order_function.h @@ -31,7 +31,7 @@ #ifndef CERES_PUBLIC_FIRST_ORDER_FUNCTION_H_ #define CERES_PUBLIC_FIRST_ORDER_FUNCTION_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { @@ -39,7 +39,7 @@ namespace ceres { // and its gradient. class CERES_EXPORT FirstOrderFunction { public: - virtual ~FirstOrderFunction() {} + virtual ~FirstOrderFunction(); // cost is never null. gradient may be null. The return value // indicates whether the evaluation was successful or not. diff --git a/extern/ceres/include/ceres/gradient_checker.h b/extern/ceres/include/ceres/gradient_checker.h index b79cf86b314..178fa2b0dd2 100644 --- a/extern/ceres/include/ceres/gradient_checker.h +++ b/extern/ceres/include/ceres/gradient_checker.h @@ -40,9 +40,12 @@ #include "ceres/cost_function.h" #include "ceres/dynamic_numeric_diff_cost_function.h" +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" #include "ceres/internal/fixed_array.h" #include "ceres/local_parameterization.h" +#include "ceres/manifold.h" #include "glog/logging.h" namespace ceres { @@ -65,19 +68,42 @@ namespace ceres { // CostFunction, and then call Probe(). Check that the return value is 'true'. class CERES_EXPORT GradientChecker { public: - // This will not take ownership of the cost function or local + // This constructor will not take ownership of the cost function or local // parameterizations. // // function: The cost function to probe. - // local_parameterizations: A vector of local parameterizations for each - // parameter. May be NULL or contain NULL pointers to indicate that the + // + // local_parameterizations: A vector of local parameterizations, one for each + // parameter block. May be nullptr or contain nullptrs to indicate that the // respective parameter does not have a local parameterization. + // // options: Options to use for numerical differentiation. + // + // NOTE: This constructor is deprecated and will be removed in the next public + // release of Ceres Solver. Please transition to using the Manifold based + // version. + CERES_DEPRECATED_WITH_MSG( + "Local Parameterizations are deprecated. Use the constructor that uses " + "Manifolds instead.") GradientChecker( const CostFunction* function, const std::vector<const LocalParameterization*>* local_parameterizations, const NumericDiffOptions& options); + // This will not take ownership of the cost function or manifolds. + // + // function: The cost function to probe. + // + // manifolds: A vector of manifolds for each parameter. May be nullptr or + // contain nullptrs to indicate that the respective parameter blocks are + // Euclidean. + // + // options: Options to use for numerical differentiation. + GradientChecker(const CostFunction* function, + const std::vector<const Manifold*>* manifolds, + const NumericDiffOptions& options); + ~GradientChecker(); + // Contains results from a call to Probe for later inspection. struct CERES_EXPORT ProbeResults { // The return value of the cost function. @@ -87,11 +113,11 @@ class CERES_EXPORT GradientChecker { Vector residuals; // The sizes of the Jacobians below are dictated by the cost function's - // parameter block size and residual block sizes. If a parameter block - // has a local parameterization associated with it, the size of the "local" - // Jacobian will be determined by the local parameterization dimension and - // residual block size, otherwise it will be identical to the regular - // Jacobian. + // parameter block size and residual block sizes. If a parameter block has a + // manifold associated with it, the size of the "local" Jacobian will be + // determined by the dimension of the manifold (which is the same as the + // dimension of the tangent space) and residual block size, otherwise it + // will be identical to the regular Jacobian. // Derivatives as computed by the cost function. std::vector<Matrix> jacobians; @@ -114,20 +140,20 @@ class CERES_EXPORT GradientChecker { }; // Call the cost function, compute alternative Jacobians using finite - // differencing and compare results. If local parameterizations are given, - // the Jacobians will be multiplied by the local parameterization Jacobians - // before performing the check, which effectively means that all errors along - // the null space of the local parameterization will be ignored. - // Returns false if the Jacobians don't match, the cost function return false, - // or if the cost function returns different residual when called with a - // Jacobian output argument vs. calling it without. Otherwise returns true. + // differencing and compare results. If manifolds are given, the Jacobians + // will be multiplied by the manifold Jacobians before performing the check, + // which effectively means that all errors along the null space of the + // manifold will be ignored. Returns false if the Jacobians don't match, the + // cost function return false, or if a cost function returns a different + // residual when called with a Jacobian output argument vs. calling it + // without. Otherwise returns true. // // parameters: The parameter values at which to probe. // relative_precision: A threshold for the relative difference between the // Jacobians. If the Jacobians differ by more than this amount, then the // probe fails. // results: On return, the Jacobians (and other information) will be stored - // here. May be NULL. + // here. May be nullptr. // // Returns true if no problems are detected and the difference between the // Jacobians is less than error_tolerance. @@ -140,11 +166,24 @@ class CERES_EXPORT GradientChecker { GradientChecker(const GradientChecker&) = delete; void operator=(const GradientChecker&) = delete; - std::vector<const LocalParameterization*> local_parameterizations_; + // This bool is used to determine whether the constructor with the + // LocalParameterizations is called or the one with Manifolds is called. If + // the former, then the vector of manifolds is a vector of ManifoldAdapter + // objects which we own and should be deleted. If the latter then they are + // real Manifold objects owned by the caller and will not be deleted. + // + // This bool is only needed during the LocalParameterization to Manifold + // transition, once this transition is complete the LocalParameterization + // based constructor and this bool will be removed. + const bool delete_manifolds_ = false; + + std::vector<const Manifold*> manifolds_; const CostFunction* function_; std::unique_ptr<CostFunction> finite_diff_cost_function_; }; } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_PUBLIC_GRADIENT_CHECKER_H_ diff --git a/extern/ceres/include/ceres/gradient_problem.h b/extern/ceres/include/ceres/gradient_problem.h index 49d605ea2d6..b6a8b867421 100644 --- a/extern/ceres/include/ceres/gradient_problem.h +++ b/extern/ceres/include/ceres/gradient_problem.h @@ -34,8 +34,10 @@ #include <memory> #include "ceres/first_order_function.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/local_parameterization.h" +#include "ceres/manifold.h" namespace ceres { @@ -43,23 +45,22 @@ class FirstOrderFunction; // Instances of GradientProblem represent general non-linear // optimization problems that must be solved using just the value of -// the objective function and its gradient. Unlike the Problem class, -// which can only be used to model non-linear least squares problems, -// instances of GradientProblem not restricted in the form of the -// objective function. +// the objective function and its gradient. + +// Unlike the Problem class, which can only be used to model non-linear least +// squares problems, instances of GradientProblem are not restricted in the form +// of the objective function. // -// Structurally GradientProblem is a composition of a -// FirstOrderFunction and optionally a LocalParameterization. +// Structurally GradientProblem is a composition of a FirstOrderFunction and +// optionally a Manifold. // -// The FirstOrderFunction is responsible for evaluating the cost and -// gradient of the objective function. +// The FirstOrderFunction is responsible for evaluating the cost and gradient of +// the objective function. // -// The LocalParameterization is responsible for going back and forth -// between the ambient space and the local tangent space. (See -// local_parameterization.h for more details). When a -// LocalParameterization is not provided, then the tangent space is -// assumed to coincide with the ambient Euclidean space that the -// gradient vector lives in. +// The Manifold is responsible for going back and forth between the ambient +// space and the local tangent space. (See manifold.h for more details). When a +// Manifold is not provided, then the tangent space is assumed to coincide with +// the ambient Euclidean space that the gradient vector lives in. // // Example usage: // @@ -78,7 +79,7 @@ class FirstOrderFunction; // const double y = parameters[1]; // // cost[0] = (1.0 - x) * (1.0 - x) + 100.0 * (y - x * x) * (y - x * x); -// if (gradient != NULL) { +// if (gradient != nullptr) { // gradient[0] = -2.0 * (1.0 - x) - 200.0 * (y - x * x) * 2.0 * x; // gradient[1] = 200.0 * (y - x * x); // } @@ -89,28 +90,96 @@ class FirstOrderFunction; // }; // // ceres::GradientProblem problem(new Rosenbrock()); +// +// NOTE: We are currently in the process of transitioning from +// LocalParameterization to Manifolds in the Ceres API. During this period, +// GradientProblem will support using both Manifold and LocalParameterization +// objects interchangably. For methods in the API affected by this change, see +// their documentation below. class CERES_EXPORT GradientProblem { public: // Takes ownership of the function. explicit GradientProblem(FirstOrderFunction* function); // Takes ownership of the function and the parameterization. + // + // NOTE: This constructor is deprecated and will be removed in the next public + // release of Ceres Solver. Please move to using the Manifold based + // constructor. + CERES_DEPRECATED_WITH_MSG( + "LocalParameterizations are deprecated. Please use the constructor that " + "uses Manifold instead.") GradientProblem(FirstOrderFunction* function, LocalParameterization* parameterization); + // Takes ownership of the function and the manifold. + GradientProblem(FirstOrderFunction* function, Manifold* manifold); + int NumParameters() const; - int NumLocalParameters() const; + + // Dimension of the manifold (and its tangent space). + // + // During the transition from LocalParameterization to Manifold, this method + // reports the LocalSize of the LocalParameterization or the TangentSize of + // the Manifold object associated with this problem. + int NumTangentParameters() const; + + // Dimension of the manifold (and its tangent space). + // + // NOTE: This method is deprecated and will be removed in the next public + // release of Ceres Solver. Please move to using NumTangentParameters() + // instead. + int NumLocalParameters() const { return NumTangentParameters(); } // This call is not thread safe. bool Evaluate(const double* parameters, double* cost, double* gradient) const; bool Plus(const double* x, const double* delta, double* x_plus_delta) const; + const FirstOrderFunction* function() const { return function_.get(); } + FirstOrderFunction* mutable_function() { return function_.get(); } + + // NOTE: During the transition from LocalParameterization to Manifold we need + // to support both The LocalParameterization and Manifold based constructors. + // + // When the user uses the LocalParameterization, internally the solver will + // wrap it in a ManifoldAdapter object and return it when manifold or + // mutable_manifold are called. + // + // As a result this method will return a non-nullptr result if a Manifold or a + // LocalParameterization was used when constructing the GradientProblem. + const Manifold* manifold() const { return manifold_.get(); } + Manifold* mutable_manifold() { return manifold_.get(); } + + // If the problem is constructed without a LocalParameterization or with a + // Manifold this method will return a nullptr. + // + // NOTE: This method is deprecated and will be removed in the next public + // release of Ceres Solver. + CERES_DEPRECATED_WITH_MSG("Use Manifolds instead.") + const LocalParameterization* parameterization() const { + return parameterization_.get(); + } + + // If the problem is constructed without a LocalParameterization or with a + // Manifold this method will return a nullptr. + // + // NOTE: This method is deprecated and will be removed in the next public + // release of Ceres Solver. + CERES_DEPRECATED_WITH_MSG("Use Manifolds instead.") + LocalParameterization* mutable_parameterization() { + return parameterization_.get(); + } + private: std::unique_ptr<FirstOrderFunction> function_; + CERES_DEPRECATED_WITH_MSG("") std::unique_ptr<LocalParameterization> parameterization_; + std::unique_ptr<Manifold> manifold_; std::unique_ptr<double[]> scratch_; }; } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_PUBLIC_GRADIENT_PROBLEM_H_ diff --git a/extern/ceres/include/ceres/gradient_problem_solver.h b/extern/ceres/include/ceres/gradient_problem_solver.h index 9fab62e6d94..b6290c80c28 100644 --- a/extern/ceres/include/ceres/gradient_problem_solver.h +++ b/extern/ceres/include/ceres/gradient_problem_solver.h @@ -36,6 +36,7 @@ #include <vector> #include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/internal/port.h" #include "ceres/iteration_callback.h" #include "ceres/types.h" @@ -305,8 +306,12 @@ class CERES_EXPORT GradientProblemSolver { int num_parameters = -1; // Dimension of the tangent space of the problem. + CERES_DEPRECATED_WITH_MSG("Use num_tangent_parameters.") int num_local_parameters = -1; + // Dimension of the tangent space of the problem. + int num_tangent_parameters = -1; + // Type of line search direction used. LineSearchDirectionType line_search_direction_type = LBFGS; diff --git a/extern/ceres/include/ceres/internal/array_selector.h b/extern/ceres/include/ceres/internal/array_selector.h index 841797f4c69..b4db012f00b 100644 --- a/extern/ceres/include/ceres/internal/array_selector.h +++ b/extern/ceres/include/ceres/internal/array_selector.h @@ -73,20 +73,22 @@ struct ArraySelector<T, true, fits_on_stack> : ceres::internal::FixedArray<T, max_num_elements_on_stack> { - ArraySelector(int s) + explicit ArraySelector(int s) : ceres::internal::FixedArray<T, max_num_elements_on_stack>(s) {} }; template <typename T, int num_elements, int max_num_elements_on_stack> struct ArraySelector<T, num_elements, max_num_elements_on_stack, false, true> : std::array<T, num_elements> { - ArraySelector(int s) { CHECK_EQ(s, num_elements); } + explicit ArraySelector(int s) { CHECK_EQ(s, num_elements); } }; template <typename T, int num_elements, int max_num_elements_on_stack> struct ArraySelector<T, num_elements, max_num_elements_on_stack, false, false> : std::vector<T> { - ArraySelector(int s) : std::vector<T>(s) { CHECK_EQ(s, num_elements); } + explicit ArraySelector(int s) : std::vector<T>(s) { + CHECK_EQ(s, num_elements); + } }; } // namespace internal diff --git a/extern/ceres/include/ceres/internal/autodiff.h b/extern/ceres/include/ceres/internal/autodiff.h index 9d7de758508..c796618cd2d 100644 --- a/extern/ceres/include/ceres/internal/autodiff.h +++ b/extern/ceres/include/ceres/internal/autodiff.h @@ -132,17 +132,16 @@ // respectively. This is how autodiff works for functors taking multiple vector // valued arguments (up to 6). // -// Jacobian NULL pointers -// ---------------------- -// In general, the functions below will accept NULL pointers for all or some of -// the Jacobian parameters, meaning that those Jacobians will not be computed. +// Jacobian null pointers (nullptr) +// -------------------------------- +// In general, the functions below will accept nullptr for all or some of the +// Jacobian parameters, meaning that those Jacobians will not be computed. #ifndef CERES_PUBLIC_INTERNAL_AUTODIFF_H_ #define CERES_PUBLIC_INTERNAL_AUTODIFF_H_ -#include <stddef.h> - #include <array> +#include <cstddef> #include <utility> #include "ceres/internal/array_selector.h" @@ -198,7 +197,7 @@ struct Make1stOrderPerturbation { template <int N, int Offset, typename T, typename JetT> struct Make1stOrderPerturbation<N, N, Offset, T, JetT> { public: - static void Apply(const T* src, JetT* dst) {} + static void Apply(const T* /* NOT USED */, JetT* /* NOT USED */) {} }; // Calls Make1stOrderPerturbation for every parameter block. @@ -311,7 +310,7 @@ inline bool AutoDifferentiate(const Functor& functor, int dynamic_num_outputs, T* function_value, T** jacobians) { - typedef Jet<T, ParameterDims::kNumParameters> JetT; + using JetT = Jet<T, ParameterDims::kNumParameters>; using Parameters = typename ParameterDims::Parameters; if (kNumResiduals != DYNAMIC) { diff --git a/extern/ceres/include/ceres/internal/eigen.h b/extern/ceres/include/ceres/internal/eigen.h index b6d0b7f610c..111cc7a07bb 100644 --- a/extern/ceres/include/ceres/internal/eigen.h +++ b/extern/ceres/include/ceres/internal/eigen.h @@ -35,39 +35,39 @@ namespace ceres { -typedef Eigen::Matrix<double, Eigen::Dynamic, 1> Vector; -typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> - Matrix; -typedef Eigen::Map<Vector> VectorRef; -typedef Eigen::Map<Matrix> MatrixRef; -typedef Eigen::Map<const Vector> ConstVectorRef; -typedef Eigen::Map<const Matrix> ConstMatrixRef; +using Vector = Eigen::Matrix<double, Eigen::Dynamic, 1>; +using Matrix = + Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>; +using VectorRef = Eigen::Map<Vector>; +using MatrixRef = Eigen::Map<Matrix>; +using ConstVectorRef = Eigen::Map<const Vector>; +using ConstMatrixRef = Eigen::Map<const Matrix>; // Column major matrices for DenseSparseMatrix/DenseQRSolver -typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor> - ColMajorMatrix; +using ColMajorMatrix = + Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>; -typedef Eigen::Map<ColMajorMatrix, 0, Eigen::Stride<Eigen::Dynamic, 1>> - ColMajorMatrixRef; +using ColMajorMatrixRef = + Eigen::Map<ColMajorMatrix, 0, Eigen::Stride<Eigen::Dynamic, 1>>; -typedef Eigen::Map<const ColMajorMatrix, 0, Eigen::Stride<Eigen::Dynamic, 1>> - ConstColMajorMatrixRef; +using ConstColMajorMatrixRef = + Eigen::Map<const ColMajorMatrix, 0, Eigen::Stride<Eigen::Dynamic, 1>>; // C++ does not support templated typdefs, thus the need for this // struct so that we can support statically sized Matrix and Maps. template <int num_rows = Eigen::Dynamic, int num_cols = Eigen::Dynamic> struct EigenTypes { - typedef Eigen::Matrix<double, - num_rows, - num_cols, - num_cols == 1 ? Eigen::ColMajor : Eigen::RowMajor> - Matrix; + using Matrix = + Eigen::Matrix<double, + num_rows, + num_cols, + num_cols == 1 ? Eigen::ColMajor : Eigen::RowMajor>; - typedef Eigen::Map<Matrix> MatrixRef; - typedef Eigen::Map<const Matrix> ConstMatrixRef; - typedef Eigen::Matrix<double, num_rows, 1> Vector; - typedef Eigen::Map<Eigen::Matrix<double, num_rows, 1>> VectorRef; - typedef Eigen::Map<const Eigen::Matrix<double, num_rows, 1>> ConstVectorRef; + using MatrixRef = Eigen::Map<Matrix>; + using ConstMatrixRef = Eigen::Map<const Matrix>; + using Vector = Eigen::Matrix<double, num_rows, 1>; + using VectorRef = Eigen::Map<Eigen::Matrix<double, num_rows, 1>>; + using ConstVectorRef = Eigen::Map<const Eigen::Matrix<double, num_rows, 1>>; }; } // namespace ceres diff --git a/extern/ceres/include/ceres/internal/householder_vector.h b/extern/ceres/include/ceres/internal/householder_vector.h index 55f68e526a0..7700208be22 100644 --- a/extern/ceres/include/ceres/internal/householder_vector.h +++ b/extern/ceres/include/ceres/internal/householder_vector.h @@ -82,6 +82,14 @@ void ComputeHouseholderVector(const XVectorType& x, v->head(v->rows() - 1) /= v_pivot; } +template <typename XVectorType, typename Derived> +typename Derived::PlainObject ApplyHouseholderVector( + const XVectorType& y, + const Eigen::MatrixBase<Derived>& v, + const typename Derived::Scalar& beta) { + return (y - v * (beta * (v.transpose() * y))); +} + } // namespace internal } // namespace ceres diff --git a/extern/ceres/include/ceres/internal/integer_sequence_algorithm.h b/extern/ceres/include/ceres/internal/integer_sequence_algorithm.h index 8c0f3bc8ac4..777c119a77f 100644 --- a/extern/ceres/include/ceres/internal/integer_sequence_algorithm.h +++ b/extern/ceres/include/ceres/internal/integer_sequence_algorithm.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2018 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -27,6 +27,7 @@ // POSSIBILITY OF SUCH DAMAGE. // // Author: jodebo_beck@gmx.de (Johannes Beck) +// sergiu.deitsch@gmail.com (Sergiu Deitsch) // // Algorithms to be used together with integer_sequence, like computing the sum // or the exclusive scan (sometimes called exclusive prefix sum) at compile @@ -37,6 +38,8 @@ #include <utility> +#include "ceres/jet_fwd.h" + namespace ceres { namespace internal { @@ -164,6 +167,124 @@ class ExclusiveScanT { template <typename Seq> using ExclusiveScan = typename ExclusiveScanT<Seq>::Type; +// Removes all elements from a integer sequence corresponding to specified +// ValueToRemove. +// +// This type should not be used directly but instead RemoveValue. +template <typename T, T ValueToRemove, typename... Sequence> +struct RemoveValueImpl; + +// Final filtered sequence +template <typename T, T ValueToRemove, T... Values> +struct RemoveValueImpl<T, + ValueToRemove, + std::integer_sequence<T, Values...>, + std::integer_sequence<T>> { + using type = std::integer_sequence<T, Values...>; +}; + +// Found a matching value +template <typename T, T ValueToRemove, T... Head, T... Tail> +struct RemoveValueImpl<T, + ValueToRemove, + std::integer_sequence<T, Head...>, + std::integer_sequence<T, ValueToRemove, Tail...>> + : RemoveValueImpl<T, + ValueToRemove, + std::integer_sequence<T, Head...>, + std::integer_sequence<T, Tail...>> {}; + +// Move one element from the tail to the head +template <typename T, T ValueToRemove, T... Head, T MiddleValue, T... Tail> +struct RemoveValueImpl<T, + ValueToRemove, + std::integer_sequence<T, Head...>, + std::integer_sequence<T, MiddleValue, Tail...>> + : RemoveValueImpl<T, + ValueToRemove, + std::integer_sequence<T, Head..., MiddleValue>, + std::integer_sequence<T, Tail...>> {}; + +// Start recursion by splitting the integer sequence into two separate ones +template <typename T, T ValueToRemove, T... Tail> +struct RemoveValueImpl<T, ValueToRemove, std::integer_sequence<T, Tail...>> + : RemoveValueImpl<T, + ValueToRemove, + std::integer_sequence<T>, + std::integer_sequence<T, Tail...>> {}; + +// RemoveValue takes an integer Sequence of arbitrary type and removes all +// elements matching ValueToRemove. +// +// In contrast to RemoveValueImpl, this implementation deduces the value type +// eliminating the need to specify it explicitly. +// +// As an example, RemoveValue<std::integer_sequence<int, 1, 2, 3>, 4>::type will +// not transform the type of the original sequence. However, +// RemoveValue<std::integer_sequence<int, 0, 0, 2>, 2>::type will generate a new +// sequence of type std::integer_sequence<int, 0, 0> by removing the value 2. +template <typename Sequence, typename Sequence::value_type ValueToRemove> +struct RemoveValue + : RemoveValueImpl<typename Sequence::value_type, ValueToRemove, Sequence> { +}; + +// Convenience template alias for RemoveValue. +template <typename Sequence, typename Sequence::value_type ValueToRemove> +using RemoveValue_t = typename RemoveValue<Sequence, ValueToRemove>::type; + +// Determines whether the values of an integer sequence are all the same. +// +// The integer sequence must contain at least one value. The predicate is +// undefined for empty sequences. The evaluation result of the predicate for a +// sequence containing only one value is defined to be true. +template <typename... Sequence> +struct AreAllEqual; + +// The predicate result for a sequence containing one element is defined to be +// true. +template <typename T, T Value> +struct AreAllEqual<std::integer_sequence<T, Value>> : std::true_type {}; + +// Recursion end. +template <typename T, T Value1, T Value2> +struct AreAllEqual<std::integer_sequence<T, Value1, Value2>> + : std::integral_constant<bool, Value1 == Value2> {}; + +// Recursion for sequences containing at least two elements. +template <typename T, T Value1, T Value2, T... Values> +// clang-format off +struct AreAllEqual<std::integer_sequence<T, Value1, Value2, Values...> > + : std::integral_constant +< + bool, + AreAllEqual<std::integer_sequence<T, Value1, Value2> >::value && + AreAllEqual<std::integer_sequence<T, Value2, Values...> >::value +> +// clang-format on +{}; + +// Convenience variable template for AreAllEqual. +template <class Sequence> +constexpr bool AreAllEqual_v = AreAllEqual<Sequence>::value; + +// Predicate determining whether an integer sequence is either empty or all +// values are equal. +template <typename Sequence> +struct IsEmptyOrAreAllEqual; + +// Empty case. +template <typename T> +struct IsEmptyOrAreAllEqual<std::integer_sequence<T>> : std::true_type {}; + +// General case for sequences containing at least one value. +template <typename T, T HeadValue, T... Values> +struct IsEmptyOrAreAllEqual<std::integer_sequence<T, HeadValue, Values...>> + : AreAllEqual<std::integer_sequence<T, HeadValue, Values...>> {}; + +// Convenience variable template for IsEmptyOrAreAllEqual. +template <class Sequence> +constexpr bool IsEmptyOrAreAllEqual_v = IsEmptyOrAreAllEqual<Sequence>::value; + } // namespace internal } // namespace ceres diff --git a/extern/ceres/include/ceres/internal/jet_traits.h b/extern/ceres/include/ceres/internal/jet_traits.h new file mode 100644 index 00000000000..2a38c05b7da --- /dev/null +++ b/extern/ceres/include/ceres/internal/jet_traits.h @@ -0,0 +1,223 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sergiu.deitsch@gmail.com (Sergiu Deitsch) +// + +#ifndef CERES_PUBLIC_INTERNAL_JET_TRAITS_H_ +#define CERES_PUBLIC_INTERNAL_JET_TRAITS_H_ + +#include <tuple> +#include <type_traits> +#include <utility> + +#include "ceres/internal/integer_sequence_algorithm.h" +#include "ceres/jet_fwd.h" + +namespace ceres { +namespace internal { + +// Predicate that determines whether T is a Jet. +template <typename T, typename E = void> +struct IsJet : std::false_type {}; + +template <typename T, int N> +struct IsJet<Jet<T, N>> : std::true_type {}; + +// Convenience variable template for IsJet. +template <typename T> +constexpr bool IsJet_v = IsJet<T>::value; + +// Predicate that determines whether any of the Types is a Jet. +template <typename... Types> +struct AreAnyJet : std::false_type {}; + +template <typename T, typename... Types> +struct AreAnyJet<T, Types...> : AreAnyJet<Types...> {}; + +template <typename T, int N, typename... Types> +struct AreAnyJet<Jet<T, N>, Types...> : std::true_type {}; + +// Convenience variable template for AreAnyJet. +template <typename... Types> +constexpr bool AreAnyJet_v = AreAnyJet<Types...>::value; + +// Extracts the underlying floating-point from a type T. +template <typename T, typename E = void> +struct UnderlyingScalar { + using type = T; +}; + +template <typename T, int N> +struct UnderlyingScalar<Jet<T, N>> : UnderlyingScalar<T> {}; + +// Convenience template alias for UnderlyingScalar type trait. +template <typename T> +using UnderlyingScalar_t = typename UnderlyingScalar<T>::type; + +// Predicate determining whether all Types in the pack are the same. +// +// Specifically, the predicate applies std::is_same recursively to pairs of +// Types in the pack. +// +// The predicate is defined only for template packs containing at least two +// types. +template <typename T1, typename T2, typename... Types> +// clang-format off +struct AreAllSame : std::integral_constant +< + bool, + AreAllSame<T1, T2>::value && + AreAllSame<T2, Types...>::value +> +// clang-format on +{}; + +// AreAllSame pairwise test. +template <typename T1, typename T2> +struct AreAllSame<T1, T2> : std::is_same<T1, T2> {}; + +// Convenience variable template for AreAllSame. +template <typename... Types> +constexpr bool AreAllSame_v = AreAllSame<Types...>::value; + +// Determines the rank of a type. This allows to ensure that types passed as +// arguments are compatible to each other. The rank of Jet is determined by the +// dimensions of the dual part. The rank of a scalar is always 0. +// Non-specialized types default to a rank of -1. +template <typename T, typename E = void> +struct Rank : std::integral_constant<int, -1> {}; + +// The rank of a scalar is 0. +template <typename T> +struct Rank<T, std::enable_if_t<std::is_scalar<T>::value>> + : std::integral_constant<int, 0> {}; + +// The rank of a Jet is given by its dimensionality. +template <typename T, int N> +struct Rank<Jet<T, N>> : std::integral_constant<int, N> {}; + +// Convenience variable template for Rank. +template <typename T> +constexpr int Rank_v = Rank<T>::value; + +// Constructs an integer sequence of ranks for each of the Types in the pack. +template <typename... Types> +using Ranks_t = std::integer_sequence<int, Rank_v<Types>...>; + +// Returns the scalar part of a type. This overload acts as an identity. +template <typename T> +constexpr decltype(auto) AsScalar(T&& value) noexcept { + return std::forward<T>(value); +} + +// Recursively unwraps the scalar part of a Jet until a non-Jet scalar type is +// encountered. +template <typename T, int N> +constexpr decltype(auto) AsScalar(const Jet<T, N>& value) noexcept( + noexcept(AsScalar(value.a))) { + return AsScalar(value.a); +} + +} // namespace internal + +// Type trait ensuring at least one of the types is a Jet, +// the underlying scalar types are the same and Jet dimensions match. +// +// The type trait can be further specialized if necessary. +// +// This trait is a candidate for a concept definition once C++20 features can +// be used. +template <typename... Types> +// clang-format off +struct CompatibleJetOperands : std::integral_constant +< + bool, + // At least one of the types is a Jet + internal::AreAnyJet_v<Types...> && + // The underlying floating-point types are exactly the same + internal::AreAllSame_v<internal::UnderlyingScalar_t<Types>...> && + // Non-zero ranks of types are equal + internal::IsEmptyOrAreAllEqual_v<internal::RemoveValue_t<internal::Ranks_t<Types...>, 0>> +> +// clang-format on +{}; + +// Single Jet operand is always compatible. +template <typename T, int N> +struct CompatibleJetOperands<Jet<T, N>> : std::true_type {}; + +// Single non-Jet operand is always incompatible. +template <typename T> +struct CompatibleJetOperands<T> : std::false_type {}; + +// Empty operands are always incompatible. +template <> +struct CompatibleJetOperands<> : std::false_type {}; + +// Convenience variable template ensuring at least one of the types is a Jet, +// the underlying scalar types are the same and Jet dimensions match. +// +// This trait is a candidate for a concept definition once C++20 features can +// be used. +template <typename... Types> +constexpr bool CompatibleJetOperands_v = CompatibleJetOperands<Types...>::value; + +// Type trait ensuring at least one of the types is a Jet, +// the underlying scalar types are compatible among each other and Jet +// dimensions match. +// +// The type trait can be further specialized if necessary. +// +// This trait is a candidate for a concept definition once C++20 features can +// be used. +template <typename... Types> +// clang-format off +struct PromotableJetOperands : std::integral_constant +< + bool, + // Types can be compatible among each other + internal::AreAnyJet_v<Types...> && + // Non-zero ranks of types are equal + internal::IsEmptyOrAreAllEqual_v<internal::RemoveValue_t<internal::Ranks_t<Types...>, 0>> +> +// clang-format on +{}; + +// Convenience variable template ensuring at least one of the types is a Jet, +// the underlying scalar types are compatible among each other and Jet +// dimensions match. +// +// This trait is a candidate for a concept definition once C++20 features can +// be used. +template <typename... Types> +constexpr bool PromotableJetOperands_v = PromotableJetOperands<Types...>::value; + +} // namespace ceres + +#endif // CERES_PUBLIC_INTERNAL_JET_TRAITS_H_ diff --git a/extern/ceres/include/ceres/internal/numeric_diff.h b/extern/ceres/include/ceres/internal/numeric_diff.h index ff7a2c345e4..351845c05fb 100644 --- a/extern/ceres/include/ceres/internal/numeric_diff.h +++ b/extern/ceres/include/ceres/internal/numeric_diff.h @@ -86,18 +86,18 @@ struct NumericDiff { (kParameterBlockSize != ceres::DYNAMIC ? kParameterBlockSize : parameter_block_size); - typedef Matrix<double, kNumResiduals, 1> ResidualVector; - typedef Matrix<double, kParameterBlockSize, 1> ParameterVector; + using ResidualVector = Matrix<double, kNumResiduals, 1>; + using ParameterVector = Matrix<double, kParameterBlockSize, 1>; // The convoluted reasoning for choosing the Row/Column major // ordering of the matrix is an artifact of the restrictions in // Eigen that prevent it from creating RowMajor matrices with a // single column. In these cases, we ask for a ColMajor matrix. - typedef Matrix<double, - kNumResiduals, - kParameterBlockSize, - (kParameterBlockSize == 1) ? ColMajor : RowMajor> - JacobianMatrix; + using JacobianMatrix = + Matrix<double, + kNumResiduals, + kParameterBlockSize, + (kParameterBlockSize == 1) ? ColMajor : RowMajor>; Map<JacobianMatrix> parameter_jacobian( jacobian, num_residuals_internal, parameter_block_size_internal); @@ -121,7 +121,7 @@ struct NumericDiff { // thus ridders_relative_initial_step_size is used. if (kMethod == RIDDERS) { min_step_size = - std::max(min_step_size, options.ridders_relative_initial_step_size); + (std::max)(min_step_size, options.ridders_relative_initial_step_size); } // For each parameter in the parameter block, use finite differences to @@ -132,7 +132,7 @@ struct NumericDiff { num_residuals_internal); for (int j = 0; j < parameter_block_size_internal; ++j) { - const double delta = std::max(min_step_size, step_size(j)); + const double delta = (std::max)(min_step_size, step_size(j)); if (kMethod == RIDDERS) { if (!EvaluateRiddersJacobianColumn(functor, @@ -184,8 +184,8 @@ struct NumericDiff { using Eigen::Map; using Eigen::Matrix; - typedef Matrix<double, kNumResiduals, 1> ResidualVector; - typedef Matrix<double, kParameterBlockSize, 1> ParameterVector; + using ResidualVector = Matrix<double, kNumResiduals, 1>; + using ParameterVector = Matrix<double, kParameterBlockSize, 1>; Map<const ParameterVector> x(x_ptr, parameter_block_size); Map<ParameterVector> x_plus_delta(x_plus_delta_ptr, parameter_block_size); @@ -260,10 +260,10 @@ struct NumericDiff { using Eigen::Map; using Eigen::Matrix; - typedef Matrix<double, kNumResiduals, 1> ResidualVector; - typedef Matrix<double, kNumResiduals, Eigen::Dynamic> - ResidualCandidateMatrix; - typedef Matrix<double, kParameterBlockSize, 1> ParameterVector; + using ResidualVector = Matrix<double, kNumResiduals, 1>; + using ResidualCandidateMatrix = + Matrix<double, kNumResiduals, Eigen::Dynamic>; + using ParameterVector = Matrix<double, kParameterBlockSize, 1>; Map<const ParameterVector> x(x_ptr, parameter_block_size); Map<ParameterVector> x_plus_delta(x_plus_delta_ptr, parameter_block_size); @@ -296,7 +296,7 @@ struct NumericDiff { // norm_error is supposed to decrease as the finite difference tableau // generation progresses, serving both as an estimate for differentiation // error and as a measure of differentiation numerical stability. - double norm_error = std::numeric_limits<double>::max(); + double norm_error = (std::numeric_limits<double>::max)(); // Loop over decreasing step sizes until: // 1. Error is smaller than a given value (ridders_epsilon), @@ -342,7 +342,7 @@ struct NumericDiff { options.ridders_step_shrink_factor; // Compute the difference between the previous value and the current. - double candidate_error = std::max( + double candidate_error = (std::max)( (current_candidates->col(k) - current_candidates->col(k - 1)) .norm(), (current_candidates->col(k) - previous_candidates->col(k - 1)) diff --git a/extern/ceres/include/ceres/internal/port.h b/extern/ceres/include/ceres/internal/port.h index 040a1efba02..4275b0e15c3 100644 --- a/extern/ceres/include/ceres/internal/port.h +++ b/extern/ceres/include/ceres/internal/port.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -31,80 +31,58 @@ #ifndef CERES_PUBLIC_INTERNAL_PORT_H_ #define CERES_PUBLIC_INTERNAL_PORT_H_ -// This file needs to compile as c code. -#include "ceres/internal/config.h" - -#if defined(CERES_USE_OPENMP) -#if defined(CERES_USE_CXX_THREADS) || defined(CERES_NO_THREADS) -#error CERES_USE_OPENMP is mutually exclusive to CERES_USE_CXX_THREADS and CERES_NO_THREADS -#endif -#elif defined(CERES_USE_CXX_THREADS) -#if defined(CERES_USE_OPENMP) || defined(CERES_NO_THREADS) -#error CERES_USE_CXX_THREADS is mutually exclusive to CERES_USE_OPENMP, CERES_USE_CXX_THREADS and CERES_NO_THREADS -#endif -#elif defined(CERES_NO_THREADS) -#if defined(CERES_USE_OPENMP) || defined(CERES_USE_CXX_THREADS) -#error CERES_NO_THREADS is mutually exclusive to CERES_USE_OPENMP and CERES_USE_CXX_THREADS -#endif -#else -# error One of CERES_USE_OPENMP, CERES_USE_CXX_THREADS or CERES_NO_THREADS must be defined. -#endif - -// CERES_NO_SPARSE should be automatically defined by config.h if Ceres was -// compiled without any sparse back-end. Verify that it has not subsequently -// been inconsistently redefined. -#if defined(CERES_NO_SPARSE) -#if !defined(CERES_NO_SUITESPARSE) -#error CERES_NO_SPARSE requires CERES_NO_SUITESPARSE. -#endif -#if !defined(CERES_NO_CXSPARSE) -#error CERES_NO_SPARSE requires CERES_NO_CXSPARSE -#endif -#if !defined(CERES_NO_ACCELERATE_SPARSE) -#error CERES_NO_SPARSE requires CERES_NO_ACCELERATE_SPARSE -#endif -#if defined(CERES_USE_EIGEN_SPARSE) -#error CERES_NO_SPARSE requires !CERES_USE_EIGEN_SPARSE -#endif -#endif - -// A macro to signal which functions and classes are exported when -// building a shared library. +// A macro to mark a function/variable/class as deprecated. +// We use compiler specific attributes rather than the c++ +// attribute because they do not mix well with each other. #if defined(_MSC_VER) -#define CERES_API_SHARED_IMPORT __declspec(dllimport) -#define CERES_API_SHARED_EXPORT __declspec(dllexport) +#define CERES_DEPRECATED_WITH_MSG(message) __declspec(deprecated(message)) #elif defined(__GNUC__) -#define CERES_API_SHARED_IMPORT __attribute__((visibility("default"))) -#define CERES_API_SHARED_EXPORT __attribute__((visibility("default"))) +#define CERES_DEPRECATED_WITH_MSG(message) __attribute__((deprecated(message))) #else -#define CERES_API_SHARED_IMPORT -#define CERES_API_SHARED_EXPORT +// In the worst case fall back to c++ attribute. +#define CERES_DEPRECATED_WITH_MSG(message) [[deprecated(message)]] #endif -// CERES_BUILDING_SHARED_LIBRARY is only defined locally when Ceres itself is -// compiled as a shared library, it is never exported to users. In order that -// we do not have to configure config.h separately when building Ceres as either -// a static or dynamic library, we define both CERES_USING_SHARED_LIBRARY and -// CERES_BUILDING_SHARED_LIBRARY when building as a shared library. -#if defined(CERES_USING_SHARED_LIBRARY) -#if defined(CERES_BUILDING_SHARED_LIBRARY) -// Compiling Ceres itself as a shared library. -#define CERES_EXPORT CERES_API_SHARED_EXPORT -#else -// Using Ceres as a shared library. -#define CERES_EXPORT CERES_API_SHARED_IMPORT -#endif -#else -// Ceres was compiled as a static library, export everything. -#define CERES_EXPORT +#ifndef CERES_GET_FLAG +#define CERES_GET_FLAG(X) X #endif -// Unit tests reach in and test internal functionality so we need a way to make -// those symbols visible -#ifdef CERES_EXPORT_INTERNAL_SYMBOLS -#define CERES_EXPORT_INTERNAL CERES_EXPORT -#else -#define CERES_EXPORT_INTERNAL -#endif +// Indicates whether C++17 is currently active +#ifndef CERES_HAS_CPP17 +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define CERES_HAS_CPP17 +#endif // __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= + // 201703L) +#endif // !defined(CERES_HAS_CPP17) + +// Indicates whether C++20 is currently active +#ifndef CERES_HAS_CPP20 +#if __cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +#define CERES_HAS_CPP20 +#endif // __cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= + // 202002L) +#endif // !defined(CERES_HAS_CPP20) + +// Prevents symbols from being substituted by the corresponding macro definition +// under the same name. For instance, min and max are defined as macros on +// Windows (unless NOMINMAX is defined) which causes compilation errors when +// defining or referencing symbols under the same name. +// +// To be robust in all cases particularly when NOMINMAX cannot be used, use this +// macro to annotate min/max declarations/definitions. Examples: +// +// int max CERES_PREVENT_MACRO_SUBSTITUTION(); +// min CERES_PREVENT_MACRO_SUBSTITUTION(a, b); +// max CERES_PREVENT_MACRO_SUBSTITUTION(a, b); +// +// NOTE: In case the symbols for which the substitution must be prevented are +// used within another macro, the substitution must be inhibited using parens as +// +// (std::numerical_limits<double>::max)() +// +// since the helper macro will not work here. Do not use this technique in +// general case, because it will prevent argument-dependent lookup (ADL). +// +#define CERES_PREVENT_MACRO_SUBSTITUTION // Yes, it's empty #endif // CERES_PUBLIC_INTERNAL_PORT_H_ diff --git a/extern/ceres/include/ceres/internal/sphere_manifold_functions.h b/extern/ceres/include/ceres/internal/sphere_manifold_functions.h new file mode 100644 index 00000000000..5be3321a579 --- /dev/null +++ b/extern/ceres/include/ceres/internal/sphere_manifold_functions.h @@ -0,0 +1,162 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: vitus@google.com (Mike Vitus) +// jodebo_beck@gmx.de (Johannes Beck) + +#ifndef CERES_PUBLIC_INTERNAL_SPHERE_MANIFOLD_HELPERS_H_ +#define CERES_PUBLIC_INTERNAL_SPHERE_MANIFOLD_HELPERS_H_ + +#include "ceres/internal/householder_vector.h" + +// This module contains functions to compute the SphereManifold plus and minus +// operator and their Jacobians. +// +// As the parameters to these functions are shared between them, they are +// described here: The following variable names are used: +// Plus(x, delta) = x + delta = x_plus_delta, +// Minus(y, x) = y - x = y_minus_x. +// +// The remaining ones are v and beta which describe the Householder +// transformation of x, and norm_delta which is the norm of delta. +// +// The types of x, y, x_plus_delta and y_minus_x need to be equivalent to +// Eigen::Matrix<double, AmbientSpaceDimension, 1> and the type of delta needs +// to be equivalent to Eigen::Matrix<double, TangentSpaceDimension, 1>. +// +// The type of Jacobian plus needs to be equivalent to Eigen::Matrix<double, +// AmbientSpaceDimension, TangentSpaceDimension, Eigen::RowMajor> and for +// Jacobian minus Eigen::Matrix<double, TangentSpaceDimension, +// AmbientSpaceDimension, Eigen::RowMajor>. +// +// For all vector / matrix inputs and outputs, template parameters are +// used in order to allow also Eigen::Ref and Eigen block expressions to +// be passed to the function. + +namespace ceres { +namespace internal { + +template <typename VT, typename XT, typename DeltaT, typename XPlusDeltaT> +inline void ComputeSphereManifoldPlus(const VT& v, + double beta, + const XT& x, + const DeltaT& delta, + double norm_delta, + XPlusDeltaT* x_plus_delta) { + constexpr int AmbientDim = VT::RowsAtCompileTime; + + // Map the delta from the minimum representation to the over parameterized + // homogeneous vector. See B.2 p.25 equation (106) - (107) for more details. + const double norm_delta_div_2 = 0.5 * norm_delta; + const double sin_delta_by_delta = + std::sin(norm_delta_div_2) / norm_delta_div_2; + + Eigen::Matrix<double, AmbientDim, 1> y(v.size()); + y << 0.5 * sin_delta_by_delta * delta, std::cos(norm_delta_div_2); + + // Apply the delta update to remain on the sphere. + *x_plus_delta = x.norm() * ApplyHouseholderVector(y, v, beta); +} + +template <typename VT, typename JacobianT> +inline void ComputeSphereManifoldPlusJacobian(const VT& x, + JacobianT* jacobian) { + constexpr int AmbientSpaceDim = VT::RowsAtCompileTime; + using AmbientVector = Eigen::Matrix<double, AmbientSpaceDim, 1>; + const int ambient_size = x.size(); + const int tangent_size = x.size() - 1; + + AmbientVector v(ambient_size); + double beta; + + // NOTE: The explicit template arguments are needed here because + // ComputeHouseholderVector is templated and some versions of MSVC + // have trouble deducing the type of v automatically. + ComputeHouseholderVector<VT, double, AmbientSpaceDim>(x, &v, &beta); + + // The Jacobian is equal to J = 0.5 * H.leftCols(size_ - 1) where H is the + // Householder matrix (H = I - beta * v * v'). + for (int i = 0; i < tangent_size; ++i) { + (*jacobian).col(i) = -0.5 * beta * v(i) * v; + (*jacobian)(i, i) += 0.5; + } + (*jacobian) *= x.norm(); +} + +template <typename VT, typename XT, typename YT, typename YMinusXT> +inline void ComputeSphereManifoldMinus( + const VT& v, double beta, const XT& x, const YT& y, YMinusXT* y_minus_x) { + constexpr int AmbientSpaceDim = VT::RowsAtCompileTime; + constexpr int TangentSpaceDim = + AmbientSpaceDim == Eigen::Dynamic ? Eigen::Dynamic : AmbientSpaceDim - 1; + using AmbientVector = Eigen::Matrix<double, AmbientSpaceDim, 1>; + + const int tanget_size = v.size() - 1; + + const AmbientVector hy = ApplyHouseholderVector(y, v, beta) / x.norm(); + + // Calculate y - x. See B.2 p.25 equation (108). + double y_last = hy[tanget_size]; + double hy_norm = hy.template head<TangentSpaceDim>(tanget_size).norm(); + if (hy_norm == 0.0) { + y_minus_x->setZero(); + } else { + *y_minus_x = 2.0 * std::atan2(hy_norm, y_last) / hy_norm * + hy.template head<TangentSpaceDim>(tanget_size); + } +} + +template <typename VT, typename JacobianT> +inline void ComputeSphereManifoldMinusJacobian(const VT& x, + JacobianT* jacobian) { + constexpr int AmbientSpaceDim = VT::RowsAtCompileTime; + using AmbientVector = Eigen::Matrix<double, AmbientSpaceDim, 1>; + const int ambient_size = x.size(); + const int tangent_size = x.size() - 1; + + AmbientVector v(ambient_size); + double beta; + + // NOTE: The explicit template arguments are needed here because + // ComputeHouseholderVector is templated and some versions of MSVC + // have trouble deducing the type of v automatically. + ComputeHouseholderVector<VT, double, AmbientSpaceDim>(x, &v, &beta); + + // The Jacobian is equal to J = 2.0 * H.leftCols(size_ - 1) where H is the + // Householder matrix (H = I - beta * v * v'). + for (int i = 0; i < tangent_size; ++i) { + (*jacobian).row(i) = -2.0 * beta * v(i) * v; + (*jacobian)(i, i) += 2.0; + } + (*jacobian) /= x.norm(); +} + +} // namespace internal +} // namespace ceres + +#endif diff --git a/extern/ceres/include/ceres/internal/variadic_evaluate.h b/extern/ceres/include/ceres/internal/variadic_evaluate.h index 47ff6b18fa0..b8408237cc3 100644 --- a/extern/ceres/include/ceres/internal/variadic_evaluate.h +++ b/extern/ceres/include/ceres/internal/variadic_evaluate.h @@ -33,8 +33,7 @@ #ifndef CERES_PUBLIC_INTERNAL_VARIADIC_EVALUATE_H_ #define CERES_PUBLIC_INTERNAL_VARIADIC_EVALUATE_H_ -#include <stddef.h> - +#include <cstddef> #include <type_traits> #include <utility> diff --git a/extern/ceres/include/ceres/iteration_callback.h b/extern/ceres/include/ceres/iteration_callback.h index 4507fdf748c..3d7e8e94f30 100644 --- a/extern/ceres/include/ceres/iteration_callback.h +++ b/extern/ceres/include/ceres/iteration_callback.h @@ -36,6 +36,7 @@ #define CERES_PUBLIC_ITERATION_CALLBACK_H_ #include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/types.h" namespace ceres { @@ -164,8 +165,6 @@ struct CERES_EXPORT IterationSummary { // explicit LoggingCallback(bool log_to_stdout) // : log_to_stdout_(log_to_stdout) {} // -// ~LoggingCallback() {} -// // CallbackReturnType operator()(const IterationSummary& summary) { // const char* kReportRowFormat = // "% 4d: f:% 8e d:% 3.2e g:% 3.2e h:% 3.2e " @@ -194,7 +193,7 @@ struct CERES_EXPORT IterationSummary { // class CERES_EXPORT IterationCallback { public: - virtual ~IterationCallback() {} + virtual ~IterationCallback(); virtual CallbackReturnType operator()(const IterationSummary& summary) = 0; }; diff --git a/extern/ceres/include/ceres/jet.h b/extern/ceres/include/ceres/jet.h index da49f32019f..fba1e2ab6e0 100644 --- a/extern/ceres/include/ceres/jet.h +++ b/extern/ceres/include/ceres/jet.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2019 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -158,20 +158,59 @@ #define CERES_PUBLIC_JET_H_ #include <cmath> +#include <complex> #include <iosfwd> #include <iostream> // NOLINT #include <limits> +#include <numeric> #include <string> +#include <type_traits> #include "Eigen/Core" +#include "ceres/internal/jet_traits.h" #include "ceres/internal/port.h" +#include "ceres/jet_fwd.h" + +// Here we provide partial specializations of std::common_type for the Jet class +// to allow determining a Jet type with a common underlying arithmetic type. +// Such an arithmetic type can be either a scalar or an another Jet. An example +// for a common type, say, between a float and a Jet<double, N> is a Jet<double, +// N> (i.e., std::common_type_t<float, ceres::Jet<double, N>> and +// ceres::Jet<double, N> refer to the same type.) +// +// The partial specialization are also used for determining compatible types by +// means of SFINAE and thus allow such types to be expressed as operands of +// logical comparison operators. Missing (partial) specialization of +// std::common_type for a particular (custom) type will therefore disable the +// use of comparison operators defined by Ceres. +// +// Since these partial specializations are used as SFINAE constraints, they +// enable standard promotion rules between various scalar types and consequently +// their use in comparison against a Jet without providing implicit +// conversions from a scalar, such as an int, to a Jet (see the implementation +// of logical comparison operators below). + +template <typename T, int N, typename U> +struct std::common_type<T, ceres::Jet<U, N>> { + using type = ceres::Jet<common_type_t<T, U>, N>; +}; + +template <typename T, int N, typename U> +struct std::common_type<ceres::Jet<T, N>, U> { + using type = ceres::Jet<common_type_t<T, U>, N>; +}; + +template <typename T, int N, typename U> +struct std::common_type<ceres::Jet<T, N>, ceres::Jet<U, N>> { + using type = ceres::Jet<common_type_t<T, U>, N>; +}; namespace ceres { template <typename T, int N> struct Jet { enum { DIMENSION = N }; - typedef T Scalar; + using Scalar = T; // Default-construct "a" because otherwise this can lead to false errors about // uninitialized uses when other classes relying on default constructed T @@ -352,19 +391,21 @@ inline Jet<T, N> operator/(const Jet<T, N>& f, T s) { return Jet<T, N>(f.a * s_inverse, f.v * s_inverse); } -// Binary comparison operators for both scalars and jets. -#define CERES_DEFINE_JET_COMPARISON_OPERATOR(op) \ - template <typename T, int N> \ - inline bool operator op(const Jet<T, N>& f, const Jet<T, N>& g) { \ - return f.a op g.a; \ - } \ - template <typename T, int N> \ - inline bool operator op(const T& s, const Jet<T, N>& g) { \ - return s op g.a; \ - } \ - template <typename T, int N> \ - inline bool operator op(const Jet<T, N>& f, const T& s) { \ - return f.a op s; \ +// Binary comparison operators for both scalars and jets. At least one of the +// operands must be a Jet. Promotable scalars (e.g., int, float, double etc.) +// can appear on either side of the operator. std::common_type_t is used as an +// SFINAE constraint to selectively enable compatible operand types. This allows +// comparison, for instance, against int literals without implicit conversion. +// In case the Jet arithmetic type is a Jet itself, a recursive expansion of Jet +// value is performed. +#define CERES_DEFINE_JET_COMPARISON_OPERATOR(op) \ + template <typename Lhs, \ + typename Rhs, \ + std::enable_if_t<PromotableJetOperands_v<Lhs, Rhs>>* = nullptr> \ + constexpr bool operator op(const Lhs& f, const Rhs& g) noexcept( \ + noexcept(internal::AsScalar(f) op internal::AsScalar(g))) { \ + using internal::AsScalar; \ + return AsScalar(f) op AsScalar(g); \ } CERES_DEFINE_JET_COMPARISON_OPERATOR(<) // NOLINT CERES_DEFINE_JET_COMPARISON_OPERATOR(<=) // NOLINT @@ -386,43 +427,138 @@ using std::atan; using std::atan2; using std::cbrt; using std::ceil; +using std::copysign; using std::cos; using std::cosh; using std::erf; using std::erfc; using std::exp; using std::exp2; +using std::expm1; +using std::fdim; using std::floor; +using std::fma; using std::fmax; using std::fmin; +using std::fpclassify; using std::hypot; using std::isfinite; using std::isinf; using std::isnan; using std::isnormal; using std::log; +using std::log10; +using std::log1p; using std::log2; +using std::norm; using std::pow; +using std::signbit; using std::sin; using std::sinh; using std::sqrt; using std::tan; using std::tanh; +// MSVC (up to 1930) defines quiet comparison functions as template functions +// which causes compilation errors due to ambiguity in the template parameter +// type resolution for using declarations in the ceres namespace. Workaround the +// issue by defining specific overload and bypass MSVC standard library +// definitions. +#if defined(_MSC_VER) +inline bool isgreater(double lhs, + double rhs) noexcept(noexcept(std::isgreater(lhs, rhs))) { + return std::isgreater(lhs, rhs); +} +inline bool isless(double lhs, + double rhs) noexcept(noexcept(std::isless(lhs, rhs))) { + return std::isless(lhs, rhs); +} +inline bool islessequal(double lhs, + double rhs) noexcept(noexcept(std::islessequal(lhs, + rhs))) { + return std::islessequal(lhs, rhs); +} +inline bool isgreaterequal(double lhs, double rhs) noexcept( + noexcept(std::isgreaterequal(lhs, rhs))) { + return std::isgreaterequal(lhs, rhs); +} +inline bool islessgreater(double lhs, double rhs) noexcept( + noexcept(std::islessgreater(lhs, rhs))) { + return std::islessgreater(lhs, rhs); +} +inline bool isunordered(double lhs, + double rhs) noexcept(noexcept(std::isunordered(lhs, + rhs))) { + return std::isunordered(lhs, rhs); +} +#else +using std::isgreater; +using std::isgreaterequal; +using std::isless; +using std::islessequal; +using std::islessgreater; +using std::isunordered; +#endif + +#ifdef CERES_HAS_CPP20 +using std::lerp; +using std::midpoint; +#endif // defined(CERES_HAS_CPP20) + // Legacy names from pre-C++11 days. // clang-format off +CERES_DEPRECATED_WITH_MSG("ceres::IsFinite will be removed in a future Ceres Solver release. Please use ceres::isfinite.") inline bool IsFinite(double x) { return std::isfinite(x); } +CERES_DEPRECATED_WITH_MSG("ceres::IsInfinite will be removed in a future Ceres Solver release. Please use ceres::isinf.") inline bool IsInfinite(double x) { return std::isinf(x); } +CERES_DEPRECATED_WITH_MSG("ceres::IsNaN will be removed in a future Ceres Solver release. Please use ceres::isnan.") inline bool IsNaN(double x) { return std::isnan(x); } +CERES_DEPRECATED_WITH_MSG("ceres::IsNormal will be removed in a future Ceres Solver release. Please use ceres::isnormal.") inline bool IsNormal(double x) { return std::isnormal(x); } // clang-format on // In general, f(a + h) ~= f(a) + f'(a) h, via the chain rule. -// abs(x + h) ~= x + h or -(x + h) +// abs(x + h) ~= abs(x) + sgn(x)h template <typename T, int N> inline Jet<T, N> abs(const Jet<T, N>& f) { - return (f.a < T(0.0) ? -f : f); + return Jet<T, N>(abs(f.a), copysign(T(1), f.a) * f.v); +} + +// copysign(a, b) composes a float with the magnitude of a and the sign of b. +// Therefore, the function can be formally defined as +// +// copysign(a, b) = sgn(b)|a| +// +// where +// +// d/dx |x| = sgn(x) +// d/dx sgn(x) = 2δ(x) +// +// sgn(x) being the signum function. Differentiating copysign(a, b) with respect +// to a and b gives: +// +// d/da sgn(b)|a| = sgn(a) sgn(b) +// d/db sgn(b)|a| = 2|a|δ(b) +// +// with the dual representation given by +// +// copysign(a + da, b + db) ~= sgn(b)|a| + (sgn(a)sgn(b) da + 2|a|δ(b) db) +// +// where δ(b) is the Dirac delta function. +template <typename T, int N> +inline Jet<T, N> copysign(const Jet<T, N>& f, const Jet<T, N> g) { + // The Dirac delta function δ(b) is undefined at b=0 (here it's + // infinite) and 0 everywhere else. + T d = fpclassify(g) == FP_ZERO ? std::numeric_limits<T>::infinity() : T(0); + T sa = copysign(T(1), f.a); // sgn(a) + T sb = copysign(T(1), g.a); // sgn(b) + // The second part of the infinitesimal is 2|a|δ(b) which is either infinity + // or 0 unless a or any of the values of the b infinitesimal are 0. In the + // latter case, the corresponding values become NaNs (multiplying 0 by + // infinity gives NaN). We drop the constant factor 2 since it does not change + // the result (its values will still be either 0, infinity or NaN). + return Jet<T, N>(copysign(f.a, g.a), sa * sb * f.v + abs(f.a) * d * g.v); } // log(a + h) ~= log(a) + h / a @@ -432,6 +568,21 @@ inline Jet<T, N> log(const Jet<T, N>& f) { return Jet<T, N>(log(f.a), f.v * a_inverse); } +// log10(a + h) ~= log10(a) + h / (a log(10)) +template <typename T, int N> +inline Jet<T, N> log10(const Jet<T, N>& f) { + // Most compilers will expand log(10) to a constant. + const T a_inverse = T(1.0) / (f.a * log(T(10.0))); + return Jet<T, N>(log10(f.a), f.v * a_inverse); +} + +// log1p(a + h) ~= log1p(a) + h / (1 + a) +template <typename T, int N> +inline Jet<T, N> log1p(const Jet<T, N>& f) { + const T a_inverse = T(1.0) / (T(1.0) + f.a); + return Jet<T, N>(log1p(f.a), f.v * a_inverse); +} + // exp(a + h) ~= exp(a) + exp(a) h template <typename T, int N> inline Jet<T, N> exp(const Jet<T, N>& f) { @@ -439,6 +590,14 @@ inline Jet<T, N> exp(const Jet<T, N>& f) { return Jet<T, N>(tmp, tmp * f.v); } +// expm1(a + h) ~= expm1(a) + exp(a) h +template <typename T, int N> +inline Jet<T, N> expm1(const Jet<T, N>& f) { + const T tmp = expm1(f.a); + const T expa = tmp + T(1.0); // exp(a) = expm1(a) + 1 + return Jet<T, N>(tmp, expa * f.v); +} + // sqrt(a + h) ~= sqrt(a) + h / (2 sqrt(a)) template <typename T, int N> inline Jet<T, N> sqrt(const Jet<T, N>& f) { @@ -565,29 +724,101 @@ inline Jet<T, N> hypot(const Jet<T, N>& x, const Jet<T, N>& y) { return Jet<T, N>(tmp, x.a / tmp * x.v + y.a / tmp * y.v); } +#ifdef CERES_HAS_CPP17 +// Like sqrt(x^2 + y^2 + z^2), +// but acts to prevent underflow/overflow for small/large x/y/z. +// Note that the function is non-smooth at x=y=z=0, +// so the derivative is undefined there. template <typename T, int N> -inline Jet<T, N> fmax(const Jet<T, N>& x, const Jet<T, N>& y) { - return x < y ? y : x; +inline Jet<T, N> hypot(const Jet<T, N>& x, + const Jet<T, N>& y, + const Jet<T, N>& z) { + // d/da sqrt(a) = 0.5 / sqrt(a) + // d/dx x^2 + y^2 + z^2 = 2x + // So by the chain rule: + // d/dx sqrt(x^2 + y^2 + z^2) + // = 0.5 / sqrt(x^2 + y^2 + z^2) * 2x + // = x / sqrt(x^2 + y^2 + z^2) + // d/dy sqrt(x^2 + y^2 + z^2) = y / sqrt(x^2 + y^2 + z^2) + // d/dz sqrt(x^2 + y^2 + z^2) = z / sqrt(x^2 + y^2 + z^2) + const T tmp = hypot(x.a, y.a, z.a); + return Jet<T, N>(tmp, x.a / tmp * x.v + y.a / tmp * y.v + z.a / tmp * z.v); } +#endif // defined(CERES_HAS_CPP17) +// Like x * y + z but rounded only once. template <typename T, int N> -inline Jet<T, N> fmin(const Jet<T, N>& x, const Jet<T, N>& y) { - return y < x ? y : x; +inline Jet<T, N> fma(const Jet<T, N>& x, + const Jet<T, N>& y, + const Jet<T, N>& z) { + // d/dx fma(x, y, z) = y + // d/dy fma(x, y, z) = x + // d/dz fma(x, y, z) = 1 + return Jet<T, N>(fma(x.a, y.a, z.a), y.a * x.v + x.a * y.v + z.v); +} + +// Returns the larger of the two arguments. NaNs are treated as missing data. +// +// NOTE: This function is NOT subject to any of the error conditions specified +// in `math_errhandling`. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline decltype(auto) fmax(const Lhs& f, const Rhs& g) { + using J = std::common_type_t<Lhs, Rhs>; + return (isnan(g) || isgreater(f, g)) ? J{f} : J{g}; +} + +// Returns the smaller of the two arguments. NaNs are treated as missing data. +// +// NOTE: This function is NOT subject to any of the error conditions specified +// in `math_errhandling`. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline decltype(auto) fmin(const Lhs& f, const Rhs& g) { + using J = std::common_type_t<Lhs, Rhs>; + return (isnan(f) || isless(g, f)) ? J{g} : J{f}; +} + +// Returns the positive difference (f - g) of two arguments and zero if f <= g. +// If at least one argument is NaN, a NaN is return. +// +// NOTE At least one of the argument types must be a Jet, the other one can be a +// scalar. In case both arguments are Jets, their dimensionality must match. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline decltype(auto) fdim(const Lhs& f, const Rhs& g) { + using J = std::common_type_t<Lhs, Rhs>; + if (isnan(f) || isnan(g)) { + return std::numeric_limits<J>::quiet_NaN(); + } + return isgreater(f, g) ? J{f - g} : J{}; } -// erf is defined as an integral that cannot be expressed analyticaly +// erf is defined as an integral that cannot be expressed analytically // however, the derivative is trivial to compute // erf(x + h) = erf(x) + h * 2*exp(-x^2)/sqrt(pi) template <typename T, int N> inline Jet<T, N> erf(const Jet<T, N>& x) { - return Jet<T, N>(erf(x.a), x.v * M_2_SQRTPI * exp(-x.a * x.a)); + // We evaluate the constant as follows: + // 2 / sqrt(pi) = 1 / sqrt(atan(1.)) + // On POSIX sytems it is defined as M_2_SQRTPI, but this is not + // portable and the type may not be T. The above expression + // evaluates to full precision with IEEE arithmetic and, since it's + // constant, the compiler can generate exactly the same code. gcc + // does so even at -O0. + return Jet<T, N>(erf(x.a), x.v * exp(-x.a * x.a) * (T(1) / sqrt(atan(T(1))))); } // erfc(x) = 1-erf(x) // erfc(x + h) = erfc(x) + h * (-2*exp(-x^2)/sqrt(pi)) template <typename T, int N> inline Jet<T, N> erfc(const Jet<T, N>& x) { - return Jet<T, N>(erfc(x.a), -x.v * M_2_SQRTPI * exp(-x.a * x.a)); + // See in erf() above for the evaluation of the constant in the derivative. + return Jet<T, N>(erfc(x.a), + -x.v * exp(-x.a * x.a) * (T(1) / sqrt(atan(T(1))))); } // Bessel functions of the first kind with integer order equal to 0, 1, n. @@ -648,80 +879,210 @@ inline Jet<T, N> BesselJn(int n, const Jet<T, N>& f) { T(0.5) * (BesselJn(n - 1, f.a) - BesselJn(n + 1, f.a)) * f.v); } -// Jet Classification. It is not clear what the appropriate semantics are for -// these classifications. This picks that std::isfinite and std::isnormal are -// "all" operations, i.e. all elements of the jet must be finite for the jet -// itself to be finite (or normal). For IsNaN and IsInfinite, the answer is less -// clear. This takes a "any" approach for IsNaN and IsInfinite such that if any -// part of a jet is nan or inf, then the entire jet is nan or inf. This leads -// to strange situations like a jet can be both IsInfinite and IsNaN, but in -// practice the "any" semantics are the most useful for e.g. checking that -// derivatives are sane. - -// The jet is finite if all parts of the jet are finite. +// Classification and comparison functionality referencing only the scalar part +// of a Jet. To classify the derivatives (e.g., for sanity checks), the dual +// part should be referenced explicitly. For instance, to check whether the +// derivatives of a Jet 'f' are reasonable, one can use +// +// isfinite(f.v.array()).all() +// !isnan(f.v.array()).any() +// +// etc., depending on the desired semantics. +// +// NOTE: Floating-point classification and comparison functions and operators +// should be used with care as no derivatives can be propagated by such +// functions directly but only by expressions resulting from corresponding +// conditional statements. At the same time, conditional statements can possibly +// introduce a discontinuity in the cost function making it impossible to +// evaluate its derivative and thus the optimization problem intractable. + +// Determines whether the scalar part of the Jet is finite. template <typename T, int N> inline bool isfinite(const Jet<T, N>& f) { - // Branchless implementation. This is more efficient for the false-case and - // works with the codegen system. - auto result = isfinite(f.a); - for (int i = 0; i < N; ++i) { - result = result & isfinite(f.v[i]); - } - return result; + return isfinite(f.a); } -// The jet is infinite if any part of the Jet is infinite. +// Determines whether the scalar part of the Jet is infinite. template <typename T, int N> inline bool isinf(const Jet<T, N>& f) { - auto result = isinf(f.a); - for (int i = 0; i < N; ++i) { - result = result | isinf(f.v[i]); - } - return result; + return isinf(f.a); } -// The jet is NaN if any part of the jet is NaN. +// Determines whether the scalar part of the Jet is NaN. template <typename T, int N> inline bool isnan(const Jet<T, N>& f) { - auto result = isnan(f.a); - for (int i = 0; i < N; ++i) { - result = result | isnan(f.v[i]); - } - return result; + return isnan(f.a); } -// The jet is normal if all parts of the jet are normal. +// Determines whether the scalar part of the Jet is neither zero, subnormal, +// infinite, nor NaN. template <typename T, int N> inline bool isnormal(const Jet<T, N>& f) { - auto result = isnormal(f.a); - for (int i = 0; i < N; ++i) { - result = result & isnormal(f.v[i]); - } - return result; + return isnormal(f.a); +} + +// Determines whether the scalar part of the Jet f is less than the scalar +// part of g. +// +// NOTE: This function does NOT set any floating-point exceptions. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline bool isless(const Lhs& f, const Rhs& g) { + using internal::AsScalar; + return isless(AsScalar(f), AsScalar(g)); +} + +// Determines whether the scalar part of the Jet f is greater than the scalar +// part of g. +// +// NOTE: This function does NOT set any floating-point exceptions. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline bool isgreater(const Lhs& f, const Rhs& g) { + using internal::AsScalar; + return isgreater(AsScalar(f), AsScalar(g)); +} + +// Determines whether the scalar part of the Jet f is less than or equal to the +// scalar part of g. +// +// NOTE: This function does NOT set any floating-point exceptions. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline bool islessequal(const Lhs& f, const Rhs& g) { + using internal::AsScalar; + return islessequal(AsScalar(f), AsScalar(g)); +} + +// Determines whether the scalar part of the Jet f is less than or greater than +// (f < g || f > g) the scalar part of g. +// +// NOTE: This function does NOT set any floating-point exceptions. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline bool islessgreater(const Lhs& f, const Rhs& g) { + using internal::AsScalar; + return islessgreater(AsScalar(f), AsScalar(g)); +} + +// Determines whether the scalar part of the Jet f is greater than or equal to +// the scalar part of g. +// +// NOTE: This function does NOT set any floating-point exceptions. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline bool isgreaterequal(const Lhs& f, const Rhs& g) { + using internal::AsScalar; + return isgreaterequal(AsScalar(f), AsScalar(g)); +} + +// Determines if either of the scalar parts of the arguments are NaN and +// thus cannot be ordered with respect to each other. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline bool isunordered(const Lhs& f, const Rhs& g) { + using internal::AsScalar; + return isunordered(AsScalar(f), AsScalar(g)); +} + +// Categorize scalar part as zero, subnormal, normal, infinite, NaN, or +// implementation-defined. +template <typename T, int N> +inline int fpclassify(const Jet<T, N>& f) { + return fpclassify(f.a); +} + +// Determines whether the scalar part of the argument is negative. +template <typename T, int N> +inline bool signbit(const Jet<T, N>& f) { + return signbit(f.a); } // Legacy functions from the pre-C++11 days. template <typename T, int N> +CERES_DEPRECATED_WITH_MSG( + "ceres::IsFinite will be removed in a future Ceres Solver release. Please " + "use ceres::isfinite.") inline bool IsFinite(const Jet<T, N>& f) { return isfinite(f); } template <typename T, int N> +CERES_DEPRECATED_WITH_MSG( + "ceres::IsNaN will be removed in a future Ceres Solver release. Please use " + "ceres::isnan.") inline bool IsNaN(const Jet<T, N>& f) { return isnan(f); } template <typename T, int N> +CERES_DEPRECATED_WITH_MSG( + "ceres::IsNormal will be removed in a future Ceres Solver release. Please " + "use ceres::isnormal.") inline bool IsNormal(const Jet<T, N>& f) { return isnormal(f); } // The jet is infinite if any part of the jet is infinite. template <typename T, int N> +CERES_DEPRECATED_WITH_MSG( + "ceres::IsInfinite will be removed in a future Ceres Solver release. " + "Please use ceres::isinf.") inline bool IsInfinite(const Jet<T, N>& f) { return isinf(f); } +#ifdef CERES_HAS_CPP20 +// Computes the linear interpolation a + t(b - a) between a and b at the value +// t. For arguments outside of the range 0 <= t <= 1, the values are +// extrapolated. +// +// Differentiating lerp(a, b, t) with respect to a, b, and t gives: +// +// d/da lerp(a, b, t) = 1 - t +// d/db lerp(a, b, t) = t +// d/dt lerp(a, b, t) = b - a +// +// with the dual representation given by +// +// lerp(a + da, b + db, t + dt) +// ~= lerp(a, b, t) + (1 - t) da + t db + (b - a) dt . +template <typename T, int N> +inline Jet<T, N> lerp(const Jet<T, N>& a, + const Jet<T, N>& b, + const Jet<T, N>& t) { + return Jet<T, N>{lerp(a.a, b.a, t.a), + (T(1) - t.a) * a.v + t.a * b.v + (b.a - a.a) * t.v}; +} + +// Computes the midpoint a + (b - a) / 2. +// +// Differentiating midpoint(a, b) with respect to a and b gives: +// +// d/da midpoint(a, b) = 1/2 +// d/db midpoint(a, b) = 1/2 +// +// with the dual representation given by +// +// midpoint(a + da, b + db) ~= midpoint(a, b) + (da + db) / 2 . +template <typename T, int N> +inline Jet<T, N> midpoint(const Jet<T, N>& a, const Jet<T, N>& b) { + Jet<T, N> result{midpoint(a.a, b.a)}; + // To avoid overflow in the differential, compute + // (da + db) / 2 using midpoint. + for (int i = 0; i < N; ++i) { + result.v[i] = midpoint(a.v[i], b.v[i]); + } + return result; +} +#endif // defined(CERES_HAS_CPP20) + // atan2(b + db, a + da) ~= atan2(b, a) + (- b da + a db) / (a^2 + b^2) // // In words: the rate of change of theta is 1/r times the rate of @@ -737,6 +1098,22 @@ inline Jet<T, N> atan2(const Jet<T, N>& g, const Jet<T, N>& f) { return Jet<T, N>(atan2(g.a, f.a), tmp * (-g.a * f.v + f.a * g.v)); } +// Computes the square x^2 of a real number x (not the Euclidean L^2 norm as +// the name might suggest). +// +// NOTE: While std::norm is primarily intended for computing the squared +// magnitude of a std::complex<> number, the current Jet implementation does not +// support mixing a scalar T in its real part and std::complex<T> and in the +// infinitesimal. Mixed Jet support is necessary for the type decay from +// std::complex<T> to T (the squared magnitude of a complex number is always +// real) performed by std::norm. +// +// norm(x + h) ~= norm(x) + 2x h +template <typename T, int N> +inline Jet<T, N> norm(const Jet<T, N>& f) { + return Jet<T, N>(norm(f.a), T(2) * f.a * f.v); +} + // pow -- base is a differentiable function, exponent is a constant. // (a+da)^p ~= a^p + p*a^(p-1) da template <typename T, int N> @@ -760,14 +1137,14 @@ template <typename T, int N> inline Jet<T, N> pow(T f, const Jet<T, N>& g) { Jet<T, N> result; - if (f == T(0) && g.a > T(0)) { + if (fpclassify(f) == FP_ZERO && g > 0) { // Handle case 2. result = Jet<T, N>(T(0.0)); } else { - if (f < 0 && g.a == floor(g.a)) { // Handle case 3. + if (f < 0 && g == floor(g.a)) { // Handle case 3. result = Jet<T, N>(pow(f, g.a)); for (int i = 0; i < N; i++) { - if (g.v[i] != T(0.0)) { + if (fpclassify(g.v[i]) != FP_ZERO) { // Return a NaN when g.v != 0. result.v[i] = std::numeric_limits<T>::quiet_NaN(); } @@ -822,21 +1199,21 @@ template <typename T, int N> inline Jet<T, N> pow(const Jet<T, N>& f, const Jet<T, N>& g) { Jet<T, N> result; - if (f.a == T(0) && g.a >= T(1)) { + if (fpclassify(f) == FP_ZERO && g >= 1) { // Handle cases 2 and 3. - if (g.a > T(1)) { + if (g > 1) { result = Jet<T, N>(T(0.0)); } else { result = f; } } else { - if (f.a < T(0) && g.a == floor(g.a)) { + if (f < 0 && g == floor(g.a)) { // Handle cases 7 and 8. T const tmp = g.a * pow(f.a, g.a - T(1.0)); result = Jet<T, N>(pow(f.a, g.a), tmp * f.v); for (int i = 0; i < N; i++) { - if (g.v[i] != T(0.0)) { + if (fpclassify(g.v[i]) != FP_ZERO) { // Return a NaN when g.v != 0. result.v[i] = T(std::numeric_limits<double>::quiet_NaN()); } @@ -904,8 +1281,9 @@ struct numeric_limits<ceres::Jet<T, N>> { static constexpr bool tinyness_before = std::numeric_limits<T>::tinyness_before; - static constexpr ceres::Jet<T, N> min() noexcept { - return ceres::Jet<T, N>(std::numeric_limits<T>::min()); + static constexpr ceres::Jet<T, N> min + CERES_PREVENT_MACRO_SUBSTITUTION() noexcept { + return ceres::Jet<T, N>((std::numeric_limits<T>::min)()); } static constexpr ceres::Jet<T, N> lowest() noexcept { return ceres::Jet<T, N>(std::numeric_limits<T>::lowest()); @@ -929,8 +1307,9 @@ struct numeric_limits<ceres::Jet<T, N>> { return ceres::Jet<T, N>(std::numeric_limits<T>::denorm_min()); } - static constexpr ceres::Jet<T, N> max() noexcept { - return ceres::Jet<T, N>(std::numeric_limits<T>::max()); + static constexpr ceres::Jet<T, N> max + CERES_PREVENT_MACRO_SUBSTITUTION() noexcept { + return ceres::Jet<T, N>((std::numeric_limits<T>::max)()); } }; @@ -942,10 +1321,10 @@ namespace Eigen { // Eigen arrays, getting all the goodness of Eigen combined with autodiff. template <typename T, int N> struct NumTraits<ceres::Jet<T, N>> { - typedef ceres::Jet<T, N> Real; - typedef ceres::Jet<T, N> NonInteger; - typedef ceres::Jet<T, N> Nested; - typedef ceres::Jet<T, N> Literal; + using Real = ceres::Jet<T, N>; + using NonInteger = ceres::Jet<T, N>; + using Nested = ceres::Jet<T, N>; + using Literal = ceres::Jet<T, N>; static typename ceres::Jet<T, N> dummy_precision() { return ceres::Jet<T, N>(1e-12); @@ -984,8 +1363,8 @@ struct NumTraits<ceres::Jet<T, N>> { }; }; - static inline Real highest() { return Real(std::numeric_limits<T>::max()); } - static inline Real lowest() { return Real(-std::numeric_limits<T>::max()); } + static inline Real highest() { return Real((std::numeric_limits<T>::max)()); } + static inline Real lowest() { return Real(-(std::numeric_limits<T>::max)()); } }; // Specifying the return type of binary operations between Jets and scalar types @@ -996,11 +1375,11 @@ struct NumTraits<ceres::Jet<T, N>> { // is only available on Eigen versions >= 3.3 template <typename BinaryOp, typename T, int N> struct ScalarBinaryOpTraits<ceres::Jet<T, N>, T, BinaryOp> { - typedef ceres::Jet<T, N> ReturnType; + using ReturnType = ceres::Jet<T, N>; }; template <typename BinaryOp, typename T, int N> struct ScalarBinaryOpTraits<T, ceres::Jet<T, N>, BinaryOp> { - typedef ceres::Jet<T, N> ReturnType; + using ReturnType = ceres::Jet<T, N>; }; } // namespace Eigen diff --git a/extern/ceres/internal/ceres/split.h b/extern/ceres/include/ceres/jet_fwd.h index f513023ec69..fbb6286958c 100644 --- a/extern/ceres/internal/ceres/split.h +++ b/extern/ceres/include/ceres/jet_fwd.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -26,27 +26,19 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -// Author: keir@google.com (Keir Mierle) - -#ifndef CERES_INTERNAL_SPLIT_H_ -#define CERES_INTERNAL_SPLIT_H_ - -#include <string> -#include <vector> +// Author: sergiu.deitsch@gmail.com (Sergiu Deitsch) +// -#include "ceres/internal/port.h" +#ifndef CERES_PUBLIC_JET_FWD_H_ +#define CERES_PUBLIC_JET_FWD_H_ namespace ceres { -namespace internal { -// Split a string using one or more character delimiters, presented as a -// nul-terminated c string. Append the components to 'result'. If there are -// consecutive delimiters, this function skips over all of them. -void SplitStringUsing(const std::string& full, - const char* delim, - std::vector<std::string>* res); +// Jet forward declaration necessary for the following partial specialization of +// std::common_type and type traits. +template <typename T, int N> +struct Jet; -} // namespace internal } // namespace ceres -#endif // CERES_INTERNAL_SPLIT_H_ +#endif // CERES_PUBLIC_JET_FWD_H_ diff --git a/extern/ceres/include/ceres/line_manifold.h b/extern/ceres/include/ceres/line_manifold.h new file mode 100644 index 00000000000..f8f1b235220 --- /dev/null +++ b/extern/ceres/include/ceres/line_manifold.h @@ -0,0 +1,304 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: jodebo_beck@gmx.de (Johannes Beck) +// + +#ifndef CERES_PUBLIC_LINE_MANIFOLD_H_ +#define CERES_PUBLIC_LINE_MANIFOLD_H_ + +#include <Eigen/Core> +#include <algorithm> +#include <array> +#include <memory> +#include <vector> + +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" +#include "ceres/internal/householder_vector.h" +#include "ceres/internal/sphere_manifold_functions.h" +#include "ceres/manifold.h" +#include "ceres/types.h" +#include "glog/logging.h" + +namespace ceres { +// This provides a manifold for lines, where the line is +// over-parameterized by an origin point and a direction vector. So the +// parameter vector size needs to be two times the ambient space dimension, +// where the first half is interpreted as the origin point and the second half +// as the direction. +// +// The plus operator for the line direction is the same as for the +// SphereManifold. The update of the origin point is +// perpendicular to the line direction before the update. +// +// This manifold is a special case of the affine Grassmannian +// manifold (see https://en.wikipedia.org/wiki/Affine_Grassmannian_(manifold)) +// for the case Graff_1(R^n). +// +// The class works with dynamic and static ambient space dimensions. If the +// ambient space dimensions is known at compile time use +// +// LineManifold<3> manifold; +// +// If the ambient space dimensions is not known at compile time the template +// parameter needs to be set to ceres::DYNAMIC and the actual dimension needs +// to be provided as a constructor argument: +// +// LineManifold<ceres::DYNAMIC> manifold(ambient_dim); +// +template <int AmbientSpaceDimension> +class LineManifold final : public Manifold { + public: + static_assert(AmbientSpaceDimension == DYNAMIC || AmbientSpaceDimension >= 2, + "The ambient space must be at least 2."); + static_assert(ceres::DYNAMIC == Eigen::Dynamic, + "ceres::DYNAMIC needs to be the same as Eigen::Dynamic."); + + LineManifold(); + explicit LineManifold(int size); + + int AmbientSize() const override { return 2 * size_; } + int TangentSize() const override { return 2 * (size_ - 1); } + bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const override; + bool PlusJacobian(const double* x, double* jacobian) const override; + bool Minus(const double* y, + const double* x, + double* y_minus_x) const override; + bool MinusJacobian(const double* x, double* jacobian) const override; + + private: + static constexpr bool IsDynamic = (AmbientSpaceDimension == ceres::DYNAMIC); + static constexpr int TangentSpaceDimension = + IsDynamic ? ceres::DYNAMIC : AmbientSpaceDimension - 1; + + static constexpr int DAmbientSpaceDimension = + IsDynamic ? ceres::DYNAMIC : 2 * AmbientSpaceDimension; + static constexpr int DTangentSpaceDimension = + IsDynamic ? ceres::DYNAMIC : 2 * TangentSpaceDimension; + + using AmbientVector = Eigen::Matrix<double, AmbientSpaceDimension, 1>; + using TangentVector = Eigen::Matrix<double, TangentSpaceDimension, 1>; + using MatrixPlusJacobian = Eigen::Matrix<double, + DAmbientSpaceDimension, + DTangentSpaceDimension, + Eigen::RowMajor>; + using MatrixMinusJacobian = Eigen::Matrix<double, + DTangentSpaceDimension, + DAmbientSpaceDimension, + Eigen::RowMajor>; + + const int size_{AmbientSpaceDimension}; +}; + +template <int AmbientSpaceDimension> +LineManifold<AmbientSpaceDimension>::LineManifold() + : size_{AmbientSpaceDimension} { + static_assert( + AmbientSpaceDimension != Eigen::Dynamic, + "The size is set to dynamic. Please call the constructor with a size."); +} + +template <int AmbientSpaceDimension> +LineManifold<AmbientSpaceDimension>::LineManifold(int size) : size_{size} { + if (AmbientSpaceDimension != Eigen::Dynamic) { + CHECK_EQ(AmbientSpaceDimension, size) + << "Specified size by template parameter differs from the supplied " + "one."; + } else { + CHECK_GT(size_, 1) + << "The size of the manifold needs to be greater than 1."; + } +} + +template <int AmbientSpaceDimension> +bool LineManifold<AmbientSpaceDimension>::Plus(const double* x_ptr, + const double* delta_ptr, + double* x_plus_delta_ptr) const { + // We seek a box plus operator of the form + // + // [o*, d*] = Plus([o, d], [delta_o, delta_d]) + // + // where o is the origin point, d is the direction vector, delta_o is + // the delta of the origin point and delta_d the delta of the direction and + // o* and d* is the updated origin point and direction. + // + // We separate the Plus operator into the origin point and directional part + // d* = Plus_d(d, delta_d) + // o* = Plus_o(o, d, delta_o) + // + // The direction update function Plus_d is the same as as the SphereManifold: + // + // d* = H_{v(d)} [0.5 sinc(0.5 |delta_d|) delta_d, cos(0.5 |delta_d|)]^T + // + // where H is the householder matrix + // H_{v} = I - (2 / |v|^2) v v^T + // and + // v(d) = d - sign(d_n) |d| e_n. + // + // The origin point update function Plus_o is defined as + // + // o* = o + H_{v(d)} [0.5 delta_o, 0]^T. + + Eigen::Map<const AmbientVector> o(x_ptr, size_); + Eigen::Map<const AmbientVector> d(x_ptr + size_, size_); + + Eigen::Map<const TangentVector> delta_o(delta_ptr, size_ - 1); + Eigen::Map<const TangentVector> delta_d(delta_ptr + size_ - 1, size_ - 1); + Eigen::Map<AmbientVector> o_plus_delta(x_plus_delta_ptr, size_); + Eigen::Map<AmbientVector> d_plus_delta(x_plus_delta_ptr + size_, size_); + + const double norm_delta_d = delta_d.norm(); + + o_plus_delta = o; + + // Shortcut for zero delta direction. + if (norm_delta_d == 0.0) { + d_plus_delta = d; + + if (delta_o.isZero(0.0)) { + return true; + } + } + + // Calculate the householder transformation which is needed for f_d and f_o. + AmbientVector v(size_); + double beta; + + // NOTE: The explicit template arguments are needed here because + // ComputeHouseholderVector is templated and some versions of MSVC + // have trouble deducing the type of v automatically. + internal::ComputeHouseholderVector<Eigen::Map<const AmbientVector>, + double, + AmbientSpaceDimension>(d, &v, &beta); + + if (norm_delta_d != 0.0) { + internal::ComputeSphereManifoldPlus( + v, beta, d, delta_d, norm_delta_d, &d_plus_delta); + } + + // The null space is in the direction of the line, so the tangent space is + // perpendicular to the line direction. This is achieved by using the + // householder matrix of the direction and allow only movements + // perpendicular to e_n. + // + // The factor of 0.5 is used to be consistent with the line direction + // update. + AmbientVector y(size_); + y << 0.5 * delta_o, 0; + o_plus_delta += internal::ApplyHouseholderVector(y, v, beta); + + return true; +} + +template <int AmbientSpaceDimension> +bool LineManifold<AmbientSpaceDimension>::PlusJacobian( + const double* x_ptr, double* jacobian_ptr) const { + Eigen::Map<const AmbientVector> d(x_ptr + size_, size_); + Eigen::Map<MatrixPlusJacobian> jacobian( + jacobian_ptr, 2 * size_, 2 * (size_ - 1)); + + // Clear the Jacobian as only half of the matrix is not zero. + jacobian.setZero(); + + auto jacobian_d = + jacobian + .template topLeftCorner<AmbientSpaceDimension, TangentSpaceDimension>( + size_, size_ - 1); + auto jacobian_o = jacobian.template bottomRightCorner<AmbientSpaceDimension, + TangentSpaceDimension>( + size_, size_ - 1); + internal::ComputeSphereManifoldPlusJacobian(d, &jacobian_d); + jacobian_o = jacobian_d; + return true; +} + +template <int AmbientSpaceDimension> +bool LineManifold<AmbientSpaceDimension>::Minus(const double* y_ptr, + const double* x_ptr, + double* y_minus_x) const { + Eigen::Map<const AmbientVector> y_o(y_ptr, size_); + Eigen::Map<const AmbientVector> y_d(y_ptr + size_, size_); + Eigen::Map<const AmbientVector> x_o(x_ptr, size_); + Eigen::Map<const AmbientVector> x_d(x_ptr + size_, size_); + + Eigen::Map<TangentVector> y_minus_x_o(y_minus_x, size_ - 1); + Eigen::Map<TangentVector> y_minus_x_d(y_minus_x + size_ - 1, size_ - 1); + + AmbientVector v(size_); + double beta; + + // NOTE: The explicit template arguments are needed here because + // ComputeHouseholderVector is templated and some versions of MSVC + // have trouble deducing the type of v automatically. + internal::ComputeHouseholderVector<Eigen::Map<const AmbientVector>, + double, + AmbientSpaceDimension>(x_d, &v, &beta); + + internal::ComputeSphereManifoldMinus(v, beta, x_d, y_d, &y_minus_x_d); + + AmbientVector delta_o = y_o - x_o; + const AmbientVector h_delta_o = + 2.0 * internal::ApplyHouseholderVector(delta_o, v, beta); + y_minus_x_o = h_delta_o.template head<TangentSpaceDimension>(size_ - 1); + + return true; +} + +template <int AmbientSpaceDimension> +bool LineManifold<AmbientSpaceDimension>::MinusJacobian( + const double* x_ptr, double* jacobian_ptr) const { + Eigen::Map<const AmbientVector> d(x_ptr + size_, size_); + Eigen::Map<MatrixMinusJacobian> jacobian( + jacobian_ptr, 2 * (size_ - 1), 2 * size_); + + // Clear the Jacobian as only half of the matrix is not zero. + jacobian.setZero(); + + auto jacobian_d = + jacobian + .template topLeftCorner<TangentSpaceDimension, AmbientSpaceDimension>( + size_ - 1, size_); + auto jacobian_o = jacobian.template bottomRightCorner<TangentSpaceDimension, + AmbientSpaceDimension>( + size_ - 1, size_); + internal::ComputeSphereManifoldMinusJacobian(d, &jacobian_d); + jacobian_o = jacobian_d; + + return true; +} + +} // namespace ceres + +// clang-format off +#include "ceres/internal/reenable_warnings.h" +// clang-format on + +#endif // CERES_PUBLIC_LINE_MANIFOLD_H_ diff --git a/extern/ceres/include/ceres/local_parameterization.h b/extern/ceres/include/ceres/local_parameterization.h index ba7579deca0..5815dd17d15 100644 --- a/extern/ceres/include/ceres/local_parameterization.h +++ b/extern/ceres/include/ceres/local_parameterization.h @@ -37,10 +37,14 @@ #include <vector> #include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/internal/port.h" namespace ceres { +// WARNING: LocalParameterizations are deprecated. They will be removed from +// Ceres Solver in version 2.2.0. Please use Manifolds instead. + // Purpose: Sometimes parameter blocks x can overparameterize a problem // // min f(x) @@ -111,7 +115,10 @@ namespace ceres { // // The class LocalParameterization defines the function Plus and its // Jacobian which is needed to compute the Jacobian of f w.r.t delta. -class CERES_EXPORT LocalParameterization { +class CERES_DEPRECATED_WITH_MSG( + "LocalParameterizations will be removed from the Ceres Solver API in " + "version 2.2.0. Use Manifolds instead.") + CERES_EXPORT LocalParameterization { public: virtual ~LocalParameterization(); @@ -120,6 +127,7 @@ class CERES_EXPORT LocalParameterization { // x_plus_delta = Plus(x, delta) // // with the condition that Plus(x, 0) = x. + // virtual bool Plus(const double* x, const double* delta, double* x_plus_delta) const = 0; @@ -152,10 +160,10 @@ class CERES_EXPORT LocalParameterization { // Some basic parameterizations // Identity Parameterization: Plus(x, delta) = x + delta -class CERES_EXPORT IdentityParameterization : public LocalParameterization { +class CERES_DEPRECATED_WITH_MSG("Use EuclideanManifold instead.") + CERES_EXPORT IdentityParameterization : public LocalParameterization { public: explicit IdentityParameterization(int size); - virtual ~IdentityParameterization() {} bool Plus(const double* x, const double* delta, double* x_plus_delta) const override; @@ -172,11 +180,11 @@ class CERES_EXPORT IdentityParameterization : public LocalParameterization { }; // Hold a subset of the parameters inside a parameter block constant. -class CERES_EXPORT SubsetParameterization : public LocalParameterization { +class CERES_DEPRECATED_WITH_MSG("Use SubsetManifold instead.") + CERES_EXPORT SubsetParameterization : public LocalParameterization { public: explicit SubsetParameterization(int size, const std::vector<int>& constant_parameters); - virtual ~SubsetParameterization() {} bool Plus(const double* x, const double* delta, double* x_plus_delta) const override; @@ -199,9 +207,9 @@ class CERES_EXPORT SubsetParameterization : public LocalParameterization { // with * being the quaternion multiplication operator. Here we assume // that the first element of the quaternion vector is the real (cos // theta) part. -class CERES_EXPORT QuaternionParameterization : public LocalParameterization { +class CERES_DEPRECATED_WITH_MSG("Use QuaternionManifold instead.") + CERES_EXPORT QuaternionParameterization : public LocalParameterization { public: - virtual ~QuaternionParameterization() {} bool Plus(const double* x, const double* delta, double* x_plus_delta) const override; @@ -221,10 +229,10 @@ class CERES_EXPORT QuaternionParameterization : public LocalParameterization { // // Plus(x, delta) = [sin(|delta|) delta / |delta|, cos(|delta|)] * x // with * being the quaternion multiplication operator. -class CERES_EXPORT EigenQuaternionParameterization +class CERES_DEPRECATED_WITH_MSG("Use EigenQuaternionManifold instead.") + CERES_EXPORT EigenQuaternionParameterization : public ceres::LocalParameterization { public: - virtual ~EigenQuaternionParameterization() {} bool Plus(const double* x, const double* delta, double* x_plus_delta) const override; @@ -234,23 +242,23 @@ class CERES_EXPORT EigenQuaternionParameterization }; // This provides a parameterization for homogeneous vectors which are commonly -// used in Structure for Motion problems. One example where they are used is -// in representing points whose triangulation is ill-conditioned. Here -// it is advantageous to use an over-parameterization since homogeneous vectors -// can represent points at infinity. +// used in Structure from Motion problems. One example where they are used is +// in representing points whose triangulation is ill-conditioned. Here it is +// advantageous to use an over-parameterization since homogeneous vectors can +// represent points at infinity. // // The plus operator is defined as // Plus(x, delta) = // [sin(0.5 * |delta|) * delta / |delta|, cos(0.5 * |delta|)] * x +// // with * defined as an operator which applies the update orthogonal to x to // remain on the sphere. We assume that the last element of x is the scalar // component. The size of the homogeneous vector is required to be greater than // 1. -class CERES_EXPORT HomogeneousVectorParameterization - : public LocalParameterization { +class CERES_DEPRECATED_WITH_MSG("Use SphereManifold instead.") CERES_EXPORT + HomogeneousVectorParameterization : public LocalParameterization { public: explicit HomogeneousVectorParameterization(int size); - virtual ~HomogeneousVectorParameterization() {} bool Plus(const double* x, const double* delta, double* x_plus_delta) const override; @@ -276,7 +284,8 @@ class CERES_EXPORT HomogeneousVectorParameterization // manifold (see https://en.wikipedia.org/wiki/Affine_Grassmannian_(manifold)) // for the case Graff_1(R^n). template <int AmbientSpaceDimension> -class LineParameterization : public LocalParameterization { +class CERES_DEPRECATED_WITH_MSG("Use LineManifold instead.") + LineParameterization : public LocalParameterization { public: static_assert(AmbientSpaceDimension >= 2, "The ambient space must be at least 2"); @@ -302,21 +311,19 @@ class LineParameterization : public LocalParameterization { // // is the local parameterization for a rigid transformation, where the // rotation is represented using a quaternion. -class CERES_EXPORT ProductParameterization : public LocalParameterization { +// +class CERES_DEPRECATED_WITH_MSG("Use ProductManifold instead.") + CERES_EXPORT ProductParameterization : public LocalParameterization { public: ProductParameterization(const ProductParameterization&) = delete; ProductParameterization& operator=(const ProductParameterization&) = delete; - virtual ~ProductParameterization() {} // // NOTE: The constructor takes ownership of the input local // parameterizations. // template <typename... LocalParams> - ProductParameterization(LocalParams*... local_params) - : local_params_(sizeof...(LocalParams)), - local_size_{0}, - global_size_{0}, - buffer_size_{0} { + explicit ProductParameterization(LocalParams*... local_params) + : local_params_(sizeof...(LocalParams)) { constexpr int kNumLocalParams = sizeof...(LocalParams); static_assert(kNumLocalParams >= 2, "At least two local parameterizations must be specified."); @@ -342,22 +349,23 @@ class CERES_EXPORT ProductParameterization : public LocalParameterization { bool Plus(const double* x, const double* delta, double* x_plus_delta) const override; - bool ComputeJacobian(const double* x, - double* jacobian) const override; + bool ComputeJacobian(const double* x, double* jacobian) const override; int GlobalSize() const override { return global_size_; } int LocalSize() const override { return local_size_; } private: std::vector<std::unique_ptr<LocalParameterization>> local_params_; - int local_size_; - int global_size_; - int buffer_size_; + int local_size_{0}; + int global_size_{0}; + int buffer_size_{0}; }; } // namespace ceres // clang-format off #include "ceres/internal/reenable_warnings.h" +// clang-format on + #include "ceres/internal/line_parameterization.h" #endif // CERES_PUBLIC_LOCAL_PARAMETERIZATION_H_ diff --git a/extern/ceres/include/ceres/loss_function.h b/extern/ceres/include/ceres/loss_function.h index 7aabf7dfce1..8a5a37ff665 100644 --- a/extern/ceres/include/ceres/loss_function.h +++ b/extern/ceres/include/ceres/loss_function.h @@ -35,7 +35,7 @@ // // For least squares problem where there are no outliers and standard // squared loss is expected, it is not necessary to create a loss -// function; instead passing a NULL to the problem when adding +// function; instead passing a nullptr to the problem when adding // residuals implies a standard squared loss. // // For least squares problems where the minimization may encounter @@ -78,6 +78,7 @@ #include <memory> #include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/types.h" #include "glog/logging.h" @@ -85,7 +86,7 @@ namespace ceres { class CERES_EXPORT LossFunction { public: - virtual ~LossFunction() {} + virtual ~LossFunction(); // For a residual vector with squared 2-norm 'sq_norm', this method // is required to fill in the value and derivatives of the loss @@ -125,10 +126,10 @@ class CERES_EXPORT LossFunction { // // At s = 0: rho = [0, 1, 0]. // -// It is not normally necessary to use this, as passing NULL for the +// It is not normally necessary to use this, as passing nullptr for the // loss function when building the problem accomplishes the same // thing. -class CERES_EXPORT TrivialLoss : public LossFunction { +class CERES_EXPORT TrivialLoss final : public LossFunction { public: void Evaluate(double, double*) const override; }; @@ -171,7 +172,7 @@ class CERES_EXPORT TrivialLoss : public LossFunction { // // The scaling parameter 'a' corresponds to 'delta' on this page: // http://en.wikipedia.org/wiki/Huber_Loss_Function -class CERES_EXPORT HuberLoss : public LossFunction { +class CERES_EXPORT HuberLoss final : public LossFunction { public: explicit HuberLoss(double a) : a_(a), b_(a * a) {} void Evaluate(double, double*) const override; @@ -187,7 +188,7 @@ class CERES_EXPORT HuberLoss : public LossFunction { // rho(s) = 2 (sqrt(1 + s) - 1). // // At s = 0: rho = [0, 1, -1 / (2 * a^2)]. -class CERES_EXPORT SoftLOneLoss : public LossFunction { +class CERES_EXPORT SoftLOneLoss final : public LossFunction { public: explicit SoftLOneLoss(double a) : b_(a * a), c_(1 / b_) {} void Evaluate(double, double*) const override; @@ -204,7 +205,7 @@ class CERES_EXPORT SoftLOneLoss : public LossFunction { // rho(s) = log(1 + s). // // At s = 0: rho = [0, 1, -1 / a^2]. -class CERES_EXPORT CauchyLoss : public LossFunction { +class CERES_EXPORT CauchyLoss final : public LossFunction { public: explicit CauchyLoss(double a) : b_(a * a), c_(1 / b_) {} void Evaluate(double, double*) const override; @@ -225,7 +226,7 @@ class CERES_EXPORT CauchyLoss : public LossFunction { // rho(s) = a atan(s / a). // // At s = 0: rho = [0, 1, 0]. -class CERES_EXPORT ArctanLoss : public LossFunction { +class CERES_EXPORT ArctanLoss final : public LossFunction { public: explicit ArctanLoss(double a) : a_(a), b_(1 / (a * a)) {} void Evaluate(double, double*) const override; @@ -264,7 +265,7 @@ class CERES_EXPORT ArctanLoss : public LossFunction { // concentrated in the range a - b to a + b. // // At s = 0: rho = [0, ~0, ~0]. -class CERES_EXPORT TolerantLoss : public LossFunction { +class CERES_EXPORT TolerantLoss final : public LossFunction { public: explicit TolerantLoss(double a, double b); void Evaluate(double, double*) const override; @@ -283,7 +284,7 @@ class CERES_EXPORT TolerantLoss : public LossFunction { // rho(s) = a^2 / 3 for s > a^2. // // At s = 0: rho = [0, 1, -2 / a^2] -class CERES_EXPORT TukeyLoss : public ceres::LossFunction { +class CERES_EXPORT TukeyLoss final : public ceres::LossFunction { public: explicit TukeyLoss(double a) : a_squared_(a * a) {} void Evaluate(double, double*) const override; @@ -294,14 +295,14 @@ class CERES_EXPORT TukeyLoss : public ceres::LossFunction { // Composition of two loss functions. The error is the result of first // evaluating g followed by f to yield the composition f(g(s)). -// The loss functions must not be NULL. -class CERES_EXPORT ComposedLoss : public LossFunction { +// The loss functions must not be nullptr. +class CERES_EXPORT ComposedLoss final : public LossFunction { public: explicit ComposedLoss(const LossFunction* f, Ownership ownership_f, const LossFunction* g, Ownership ownership_g); - virtual ~ComposedLoss(); + ~ComposedLoss() override; void Evaluate(double, double*) const override; private: @@ -322,11 +323,11 @@ class CERES_EXPORT ComposedLoss : public LossFunction { // s -> a * rho'(s) // s -> a * rho''(s) // -// Since we treat the a NULL Loss function as the Identity loss -// function, rho = NULL is a valid input and will result in the input +// Since we treat the a nullptr Loss function as the Identity loss +// function, rho = nullptr is a valid input and will result in the input // being scaled by a. This provides a simple way of implementing a // scaled ResidualBlock. -class CERES_EXPORT ScaledLoss : public LossFunction { +class CERES_EXPORT ScaledLoss final : public LossFunction { public: // Constructs a ScaledLoss wrapping another loss function. Takes // ownership of the wrapped loss function or not depending on the @@ -336,7 +337,7 @@ class CERES_EXPORT ScaledLoss : public LossFunction { ScaledLoss(const ScaledLoss&) = delete; void operator=(const ScaledLoss&) = delete; - virtual ~ScaledLoss() { + ~ScaledLoss() override { if (ownership_ == DO_NOT_TAKE_OWNERSHIP) { rho_.release(); } @@ -361,8 +362,8 @@ class CERES_EXPORT ScaledLoss : public LossFunction { // whose scale can be mutated after an optimization problem has been // constructed. // -// Since we treat the a NULL Loss function as the Identity loss -// function, rho = NULL is a valid input. +// Since we treat the a nullptr Loss function as the Identity loss +// function, rho = nullptr is a valid input. // // Example usage // @@ -374,7 +375,8 @@ class CERES_EXPORT ScaledLoss : public LossFunction { // new AutoDiffCostFunction < UW_Camera_Mapper, 2, 9, 3>( // new UW_Camera_Mapper(feature_x, feature_y)); // -// LossFunctionWrapper* loss_function(new HuberLoss(1.0), TAKE_OWNERSHIP); +// LossFunctionWrapper* loss_function = new LossFunctionWrapper( +// new HuberLoss(1.0), TAKE_OWNERSHIP); // // problem.AddResidualBlock(cost_function, loss_function, parameters); // @@ -387,7 +389,7 @@ class CERES_EXPORT ScaledLoss : public LossFunction { // // Solve(options, &problem, &summary) // -class CERES_EXPORT LossFunctionWrapper : public LossFunction { +class CERES_EXPORT LossFunctionWrapper final : public LossFunction { public: LossFunctionWrapper(LossFunction* rho, Ownership ownership) : rho_(rho), ownership_(ownership) {} @@ -395,14 +397,14 @@ class CERES_EXPORT LossFunctionWrapper : public LossFunction { LossFunctionWrapper(const LossFunctionWrapper&) = delete; void operator=(const LossFunctionWrapper&) = delete; - virtual ~LossFunctionWrapper() { + ~LossFunctionWrapper() override { if (ownership_ == DO_NOT_TAKE_OWNERSHIP) { rho_.release(); } } void Evaluate(double sq_norm, double out[3]) const override { - if (rho_.get() == NULL) { + if (rho_.get() == nullptr) { out[0] = sq_norm; out[1] = 1.0; out[2] = 0.0; diff --git a/extern/ceres/include/ceres/manifold.h b/extern/ceres/include/ceres/manifold.h new file mode 100644 index 00000000000..4d6e9fa0f59 --- /dev/null +++ b/extern/ceres/include/ceres/manifold.h @@ -0,0 +1,411 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#ifndef CERES_PUBLIC_MANIFOLD_H_ +#define CERES_PUBLIC_MANIFOLD_H_ + +#include <Eigen/Core> +#include <algorithm> +#include <array> +#include <memory> +#include <utility> +#include <vector> + +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" +#include "ceres/types.h" +#include "glog/logging.h" + +namespace ceres { + +// In sensor fusion problems, often we have to model quantities that live in +// spaces known as Manifolds, for example the rotation/orientation of a sensor +// that is represented by a quaternion. +// +// Manifolds are spaces which locally look like Euclidean spaces. More +// precisely, at each point on the manifold there is a linear space that is +// tangent to the manifold. It has dimension equal to the intrinsic dimension of +// the manifold itself, which is less than or equal to the ambient space in +// which the manifold is embedded. +// +// For example, the tangent space to a point on a sphere in three dimensions is +// the two dimensional plane that is tangent to the sphere at that point. There +// are two reasons tangent spaces are interesting: +// +// 1. They are Eucliean spaces so the usual vector space operations apply there, +// which makes numerical operations easy. +// 2. Movement in the tangent space translate into movements along the manifold. +// Movements perpendicular to the tangent space do not translate into +// movements on the manifold. +// +// Returning to our sphere example, moving in the 2 dimensional plane +// tangent to the sphere and projecting back onto the sphere will move you away +// from the point you started from but moving along the normal at the same point +// and the projecting back onto the sphere brings you back to the point. +// +// The Manifold interface defines two operations (and their derivatives) +// involving the tangent space, allowing filtering and optimization to be +// performed on said manifold: +// +// 1. x_plus_delta = Plus(x, delta) +// 2. delta = Minus(x_plus_delta, x) +// +// "Plus" computes the result of moving along delta in the tangent space at x, +// and then projecting back onto the manifold that x belongs to. In Differential +// Geometry this is known as a "Retraction". It is a generalization of vector +// addition in Euclidean spaces. +// +// Given two points on the manifold, "Minus" computes the change delta to x in +// the tangent space at x, that will take it to x_plus_delta. +// +// Let us now consider two examples. +// +// The Euclidean space R^n is the simplest example of a manifold. It has +// dimension n (and so does its tangent space) and Plus and Minus are the +// familiar vector sum and difference operations. +// +// Plus(x, delta) = x + delta = y, +// Minus(y, x) = y - x = delta. +// +// A more interesting case is SO(3), the special orthogonal group in three +// dimensions - the space of 3x3 rotation matrices. SO(3) is a three dimensional +// manifold embedded in R^9 or R^(3x3). So points on SO(3) are represented using +// 9 dimensional vectors or 3x3 matrices, and points in its tangent spaces are +// represented by 3 dimensional vectors. +// +// Defining Plus and Minus are defined in terms of the matrix Exp and Log +// operations as follows: +// +// Let Exp(p, q, r) = [cos(theta) + cp^2, -sr + cpq , sq + cpr ] +// [sr + cpq , cos(theta) + cq^2, -sp + cqr ] +// [-sq + cpr , sp + cqr , cos(theta) + cr^2] +// +// where: theta = sqrt(p^2 + q^2 + r^2) +// s = sinc(theta) +// c = (1 - cos(theta))/theta^2 +// +// and Log(x) = 1/(2 sinc(theta))[x_32 - x_23, x_13 - x_31, x_21 - x_12] +// +// where: theta = acos((Trace(x) - 1)/2) +// +// Then, +// +// Plus(x, delta) = x Exp(delta) +// Minus(y, x) = Log(x^T y) +// +// For Plus and Minus to be mathematically consistent, the following identities +// must be satisfied at all points x on the manifold: +// +// 1. Plus(x, 0) = x. +// 2. For all y, Plus(x, Minus(y, x)) = y. +// 3. For all delta, Minus(Plus(x, delta), x) = delta. +// 4. For all delta_1, delta_2 +// |Minus(Plus(x, delta_1), Plus(x, delta_2)) <= |delta_1 - delta_2| +// +// Briefly: +// (1) Ensures that the tangent space is "centered" at x, and the zero vector is +// the identity element. +// (2) Ensures that any y can be reached from x. +// (3) Ensures that Plus is an injective (one-to-one) map. +// (4) Allows us to define a metric on the manifold. +// +// Additionally we require that Plus and Minus be sufficiently smooth. In +// particular they need to be differentiable everywhere on the manifold. +// +// For more details, please see +// +// "Integrating Generic Sensor Fusion Algorithms with Sound State +// Representations through Encapsulation of Manifolds" +// By C. Hertzberg, R. Wagner, U. Frese and L. Schroder +// https://arxiv.org/pdf/1107.1119.pdf +class CERES_EXPORT Manifold { + public: + virtual ~Manifold(); + + // Dimension of the ambient space in which the manifold is embedded. + virtual int AmbientSize() const = 0; + + // Dimension of the manifold/tangent space. + virtual int TangentSize() const = 0; + + // x_plus_delta = Plus(x, delta), + // + // A generalization of vector addition in Euclidean space, Plus computes the + // result of moving along delta in the tangent space at x, and then projecting + // back onto the manifold that x belongs to. + // + // x and x_plus_delta are AmbientSize() vectors. + // delta is a TangentSize() vector. + // + // Return value indicates if the operation was successful or not. + virtual bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const = 0; + + // Compute the derivative of Plus(x, delta) w.r.t delta at delta = 0, i.e. + // + // (D_2 Plus)(x, 0) + // + // jacobian is a row-major AmbientSize() x TangentSize() matrix. + // + // Return value indicates whether the operation was successful or not. + virtual bool PlusJacobian(const double* x, double* jacobian) const = 0; + + // tangent_matrix = ambient_matrix * (D_2 Plus)(x, 0) + // + // ambient_matrix is a row-major num_rows x AmbientSize() matrix. + // tangent_matrix is a row-major num_rows x TangentSize() matrix. + // + // Return value indicates whether the operation was successful or not. + // + // This function is only used by the GradientProblemSolver, where the + // dimension of the parameter block can be large and it may be more efficient + // to compute this product directly rather than first evaluating the Jacobian + // into a matrix and then doing a matrix vector product. + // + // Because this is not an often used function, we provide a default + // implementation for convenience. If performance becomes an issue then the + // user should consider implementing a specialization. + virtual bool RightMultiplyByPlusJacobian(const double* x, + const int num_rows, + const double* ambient_matrix, + double* tangent_matrix) const; + + // y_minus_x = Minus(y, x) + // + // Given two points on the manifold, Minus computes the change to x in the + // tangent space at x, that will take it to y. + // + // x and y are AmbientSize() vectors. + // y_minus_x is a TangentSize() vector. + // + // Return value indicates if the operation was successful or not. + virtual bool Minus(const double* y, + const double* x, + double* y_minus_x) const = 0; + + // Compute the derivative of Minus(y, x) w.r.t y at y = x, i.e + // + // (D_1 Minus) (x, x) + // + // Jacobian is a row-major TangentSize() x AmbientSize() matrix. + // + // Return value indicates whether the operation was successful or not. + virtual bool MinusJacobian(const double* x, double* jacobian) const = 0; +}; + +// The Euclidean manifold is another name for the ordinary vector space R^size, +// where the plus and minus operations are the usual vector addition and +// subtraction: +// Plus(x, delta) = x + delta +// Minus(y, x) = y - x. +// +// The class works with dynamic and static ambient space dimensions. If the +// ambient space dimensions is know at compile time use +// +// EuclideanManifold<3> manifold; +// +// If the ambient space dimensions is not known at compile time the template +// parameter needs to be set to ceres::DYNAMIC and the actual dimension needs +// to be provided as a constructor argument: +// +// EuclideanManifold<ceres::DYNAMIC> manifold(ambient_dim); +template <int Size> +class EuclideanManifold final : public Manifold { + public: + static_assert(Size == ceres::DYNAMIC || Size >= 0, + "The size of the manifold needs to be non-negative."); + static_assert(ceres::DYNAMIC == Eigen::Dynamic, + "ceres::DYNAMIC needs to be the same as Eigen::Dynamic."); + + EuclideanManifold() : size_{Size} { + static_assert( + Size != ceres::DYNAMIC, + "The size is set to dynamic. Please call the constructor with a size."); + } + + explicit EuclideanManifold(int size) : size_(size) { + if (Size != ceres::DYNAMIC) { + CHECK_EQ(Size, size) + << "Specified size by template parameter differs from the supplied " + "one."; + } else { + CHECK_GE(size_, 0) + << "The size of the manifold needs to be non-negative."; + } + } + + int AmbientSize() const override { return size_; } + int TangentSize() const override { return size_; } + + bool Plus(const double* x_ptr, + const double* delta_ptr, + double* x_plus_delta_ptr) const override { + Eigen::Map<const AmbientVector> x(x_ptr, size_); + Eigen::Map<const AmbientVector> delta(delta_ptr, size_); + Eigen::Map<AmbientVector> x_plus_delta(x_plus_delta_ptr, size_); + x_plus_delta = x + delta; + return true; + } + + bool PlusJacobian(const double* x_ptr, double* jacobian_ptr) const override { + Eigen::Map<MatrixJacobian> jacobian(jacobian_ptr, size_, size_); + jacobian.setIdentity(); + return true; + } + + bool RightMultiplyByPlusJacobian(const double* x, + const int num_rows, + const double* ambient_matrix, + double* tangent_matrix) const override { + std::copy_n(ambient_matrix, num_rows * size_, tangent_matrix); + return true; + } + + bool Minus(const double* y_ptr, + const double* x_ptr, + double* y_minus_x_ptr) const override { + Eigen::Map<const AmbientVector> x(x_ptr, size_); + Eigen::Map<const AmbientVector> y(y_ptr, size_); + Eigen::Map<AmbientVector> y_minus_x(y_minus_x_ptr, size_); + y_minus_x = y - x; + return true; + } + + bool MinusJacobian(const double* x_ptr, double* jacobian_ptr) const override { + Eigen::Map<MatrixJacobian> jacobian(jacobian_ptr, size_, size_); + jacobian.setIdentity(); + return true; + } + + private: + static constexpr bool IsDynamic = (Size == ceres::DYNAMIC); + using AmbientVector = Eigen::Matrix<double, Size, 1>; + using MatrixJacobian = Eigen::Matrix<double, Size, Size, Eigen::RowMajor>; + + int size_{}; +}; + +// Hold a subset of the parameters inside a parameter block constant. +class CERES_EXPORT SubsetManifold final : public Manifold { + public: + SubsetManifold(int size, const std::vector<int>& constant_parameters); + int AmbientSize() const override; + int TangentSize() const override; + + bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const override; + bool PlusJacobian(const double* x, double* jacobian) const override; + bool RightMultiplyByPlusJacobian(const double* x, + const int num_rows, + const double* ambient_matrix, + double* tangent_matrix) const override; + bool Minus(const double* y, + const double* x, + double* y_minus_x) const override; + bool MinusJacobian(const double* x, double* jacobian) const override; + + private: + const int tangent_size_ = 0; + std::vector<bool> constancy_mask_; +}; + +// Implements the manifold for a Hamilton quaternion as defined in +// https://en.wikipedia.org/wiki/Quaternion. Quaternions are represented as +// unit norm 4-vectors, i.e. +// +// q = [q0; q1; q2; q3], |q| = 1 +// +// is the ambient space representation. +// +// q0 scalar part. +// q1 coefficient of i. +// q2 coefficient of j. +// q3 coefficient of k. +// +// where: i*i = j*j = k*k = -1 and i*j = k, j*k = i, k*i = j. +// +// The tangent space is R^3, which relates to the ambient space through the +// Plus and Minus operations defined as: +// +// Plus(x, delta) = [cos(|delta|); sin(|delta|) * delta / |delta|] * x +// Minus(y, x) = to_delta(y * x^{-1}) +// +// where "*" is the quaternion product and because q is a unit quaternion +// (|q|=1), q^-1 = [q0; -q1; -q2; -q3] +// +// and to_delta( [q0; u_{3x1}] ) = u / |u| * atan2(|u|, q0) +class CERES_EXPORT QuaternionManifold final : public Manifold { + public: + int AmbientSize() const override { return 4; } + int TangentSize() const override { return 3; } + + bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const override; + bool PlusJacobian(const double* x, double* jacobian) const override; + bool Minus(const double* y, + const double* x, + double* y_minus_x) const override; + bool MinusJacobian(const double* x, double* jacobian) const override; +}; + +// Implements the quaternion manifold for Eigen's representation of the +// Hamilton quaternion. Geometrically it is exactly the same as the +// QuaternionManifold defined above. However, Eigen uses a different internal +// memory layout for the elements of the quaternion than what is commonly +// used. It stores the quaternion in memory as [q1, q2, q3, q0] or +// [x, y, z, w] where the real (scalar) part is last. +// +// Since Ceres operates on parameter blocks which are raw double pointers this +// difference is important and requires a different manifold. +class CERES_EXPORT EigenQuaternionManifold final : public Manifold { + public: + int AmbientSize() const override { return 4; } + int TangentSize() const override { return 3; } + + bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const override; + bool PlusJacobian(const double* x, double* jacobian) const override; + bool Minus(const double* y, + const double* x, + double* y_minus_x) const override; + bool MinusJacobian(const double* x, double* jacobian) const override; +}; + +} // namespace ceres + +// clang-format off +#include "ceres/internal/reenable_warnings.h" +// clang-format on + +#endif // CERES_PUBLIC_MANIFOLD_H_ diff --git a/extern/ceres/include/ceres/manifold_test_utils.h b/extern/ceres/include/ceres/manifold_test_utils.h new file mode 100644 index 00000000000..3f9fb21e8f3 --- /dev/null +++ b/extern/ceres/include/ceres/manifold_test_utils.h @@ -0,0 +1,328 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#include <cmath> +#include <limits> +#include <memory> + +#include "ceres/dynamic_numeric_diff_cost_function.h" +#include "ceres/internal/eigen.h" +#include "ceres/manifold.h" +#include "ceres/numeric_diff_options.h" +#include "ceres/types.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace ceres { + +// Matchers and macros for help with testing Manifold objects. +// +// Testing a Manifold has two parts. +// +// 1. Checking that Manifold::Plus is correctly defined. This requires per +// manifold tests. +// +// 2. The other methods of the manifold have mathematical properties that make +// it compatible with Plus, as described in: +// +// "Integrating Generic Sensor Fusion Algorithms with Sound State +// Representations through Encapsulation of Manifolds" +// By C. Hertzberg, R. Wagner, U. Frese and L. Schroder +// https://arxiv.org/pdf/1107.1119.pdf +// +// These tests are implemented using generic matchers defined below which can +// all be called by the macro EXPECT_THAT_MANIFOLD_INVARIANTS_HOLD(manifold, x, +// delta, y, tolerance). See manifold_test.cc for example usage. + +// Checks that the invariant Plus(x, 0) == x holds. +MATCHER_P2(XPlusZeroIsXAt, x, tolerance, "") { + const int ambient_size = arg.AmbientSize(); + const int tangent_size = arg.TangentSize(); + + Vector actual = Vector::Zero(ambient_size); + Vector zero = Vector::Zero(tangent_size); + EXPECT_TRUE(arg.Plus(x.data(), zero.data(), actual.data())); + const double n = (actual - x).norm(); + const double d = x.norm(); + const double diffnorm = (d == 0.0) ? n : (n / d); + if (diffnorm > tolerance) { + *result_listener << "\nexpected (x): " << x.transpose() + << "\nactual: " << actual.transpose() + << "\ndiffnorm: " << diffnorm; + return false; + } + return true; +} + +// Checks that the invariant Minus(x, x) == 0 holds. +MATCHER_P2(XMinusXIsZeroAt, x, tolerance, "") { + const int tangent_size = arg.TangentSize(); + Vector actual = Vector::Zero(tangent_size); + EXPECT_TRUE(arg.Minus(x.data(), x.data(), actual.data())); + const double diffnorm = actual.norm(); + if (diffnorm > tolerance) { + *result_listener << "\nx: " << x.transpose() // + << "\nexpected: 0 0 0" + << "\nactual: " << actual.transpose() + << "\ndiffnorm: " << diffnorm; + return false; + } + return true; +} + +// Helper struct to curry Plus(x, .) so that it can be numerically +// differentiated. +struct PlusFunctor { + PlusFunctor(const Manifold& manifold, const double* x) + : manifold(manifold), x(x) {} + bool operator()(double const* const* parameters, double* x_plus_delta) const { + return manifold.Plus(x, parameters[0], x_plus_delta); + } + + const Manifold& manifold; + const double* x; +}; + +// Checks that the output of PlusJacobian matches the one obtained by +// numerically evaluating D_2 Plus(x,0). +MATCHER_P2(HasCorrectPlusJacobianAt, x, tolerance, "") { + const int ambient_size = arg.AmbientSize(); + const int tangent_size = arg.TangentSize(); + + NumericDiffOptions options; + options.ridders_relative_initial_step_size = 1e-4; + + DynamicNumericDiffCostFunction<PlusFunctor, RIDDERS> cost_function( + new PlusFunctor(arg, x.data()), TAKE_OWNERSHIP, options); + cost_function.AddParameterBlock(tangent_size); + cost_function.SetNumResiduals(ambient_size); + + Vector zero = Vector::Zero(tangent_size); + double* parameters[1] = {zero.data()}; + + Vector x_plus_zero = Vector::Zero(ambient_size); + Matrix expected = Matrix::Zero(ambient_size, tangent_size); + double* jacobians[1] = {expected.data()}; + + EXPECT_TRUE( + cost_function.Evaluate(parameters, x_plus_zero.data(), jacobians)); + + Matrix actual = Matrix::Random(ambient_size, tangent_size); + EXPECT_TRUE(arg.PlusJacobian(x.data(), actual.data())); + + const double n = (actual - expected).norm(); + const double d = expected.norm(); + const double diffnorm = (d == 0.0) ? n : n / d; + if (diffnorm > tolerance) { + *result_listener << "\nx: " << x.transpose() << "\nexpected: \n" + << expected << "\nactual:\n" + << actual << "\ndiff:\n" + << expected - actual << "\ndiffnorm : " << diffnorm; + return false; + } + return true; +} + +// Checks that the invariant Minus(Plus(x, delta), x) == delta holds. +MATCHER_P3(MinusPlusIsIdentityAt, x, delta, tolerance, "") { + const int ambient_size = arg.AmbientSize(); + const int tangent_size = arg.TangentSize(); + Vector x_plus_delta = Vector::Zero(ambient_size); + EXPECT_TRUE(arg.Plus(x.data(), delta.data(), x_plus_delta.data())); + Vector actual = Vector::Zero(tangent_size); + EXPECT_TRUE(arg.Minus(x_plus_delta.data(), x.data(), actual.data())); + + const double n = (actual - delta).norm(); + const double d = delta.norm(); + const double diffnorm = (d == 0.0) ? n : (n / d); + if (diffnorm > tolerance) { + *result_listener << "\nx: " << x.transpose() + << "\nexpected: " << delta.transpose() + << "\nactual:" << actual.transpose() + << "\ndiff:" << (delta - actual).transpose() + << "\ndiffnorm: " << diffnorm; + return false; + } + return true; +} + +// Checks that the invariant Plus(Minus(y, x), x) == y holds. +MATCHER_P3(PlusMinusIsIdentityAt, x, y, tolerance, "") { + const int ambient_size = arg.AmbientSize(); + const int tangent_size = arg.TangentSize(); + + Vector y_minus_x = Vector::Zero(tangent_size); + EXPECT_TRUE(arg.Minus(y.data(), x.data(), y_minus_x.data())); + + Vector actual = Vector::Zero(ambient_size); + EXPECT_TRUE(arg.Plus(x.data(), y_minus_x.data(), actual.data())); + + const double n = (actual - y).norm(); + const double d = y.norm(); + const double diffnorm = (d == 0.0) ? n : (n / d); + if (diffnorm > tolerance) { + *result_listener << "\nx: " << x.transpose() + << "\nexpected: " << y.transpose() + << "\nactual:" << actual.transpose() + << "\ndiff:" << (y - actual).transpose() + << "\ndiffnorm: " << diffnorm; + return false; + } + return true; +} + +// Helper struct to curry Minus(., x) so that it can be numerically +// differentiated. +struct MinusFunctor { + MinusFunctor(const Manifold& manifold, const double* x) + : manifold(manifold), x(x) {} + bool operator()(double const* const* parameters, double* y_minus_x) const { + return manifold.Minus(parameters[0], x, y_minus_x); + } + + const Manifold& manifold; + const double* x; +}; + +// Checks that the output of MinusJacobian matches the one obtained by +// numerically evaluating D_1 Minus(x,x). +MATCHER_P2(HasCorrectMinusJacobianAt, x, tolerance, "") { + const int ambient_size = arg.AmbientSize(); + const int tangent_size = arg.TangentSize(); + + Vector y = x; + Vector y_minus_x = Vector::Zero(tangent_size); + + NumericDiffOptions options; + options.ridders_relative_initial_step_size = 1e-4; + DynamicNumericDiffCostFunction<MinusFunctor, RIDDERS> cost_function( + new MinusFunctor(arg, x.data()), TAKE_OWNERSHIP, options); + cost_function.AddParameterBlock(ambient_size); + cost_function.SetNumResiduals(tangent_size); + + double* parameters[1] = {y.data()}; + + Matrix expected = Matrix::Zero(tangent_size, ambient_size); + double* jacobians[1] = {expected.data()}; + + EXPECT_TRUE(cost_function.Evaluate(parameters, y_minus_x.data(), jacobians)); + + Matrix actual = Matrix::Random(tangent_size, ambient_size); + EXPECT_TRUE(arg.MinusJacobian(x.data(), actual.data())); + + const double n = (actual - expected).norm(); + const double d = expected.norm(); + const double diffnorm = (d == 0.0) ? n : (n / d); + if (diffnorm > tolerance) { + *result_listener << "\nx: " << x.transpose() << "\nexpected: \n" + << expected << "\nactual:\n" + << actual << "\ndiff:\n" + << expected - actual << "\ndiffnorm: " << diffnorm; + return false; + } + return true; +} + +// Checks that D_delta Minus(Plus(x, delta), x) at delta = 0 is an identity +// matrix. +MATCHER_P2(MinusPlusJacobianIsIdentityAt, x, tolerance, "") { + const int ambient_size = arg.AmbientSize(); + const int tangent_size = arg.TangentSize(); + + Matrix plus_jacobian(ambient_size, tangent_size); + EXPECT_TRUE(arg.PlusJacobian(x.data(), plus_jacobian.data())); + Matrix minus_jacobian(tangent_size, ambient_size); + EXPECT_TRUE(arg.MinusJacobian(x.data(), minus_jacobian.data())); + + const Matrix actual = minus_jacobian * plus_jacobian; + const Matrix expected = Matrix::Identity(tangent_size, tangent_size); + + const double n = (actual - expected).norm(); + const double d = expected.norm(); + const double diffnorm = n / d; + if (diffnorm > tolerance) { + *result_listener << "\nx: " << x.transpose() << "\nexpected: \n" + << expected << "\nactual:\n" + << actual << "\ndiff:\n" + << expected - actual << "\ndiffnorm: " << diffnorm; + + return false; + } + return true; +} + +// Verify that the output of RightMultiplyByPlusJacobian is ambient_matrix * +// plus_jacobian. +MATCHER_P2(HasCorrectRightMultiplyByPlusJacobianAt, x, tolerance, "") { + const int ambient_size = arg.AmbientSize(); + const int tangent_size = arg.TangentSize(); + + constexpr int kMinNumRows = 0; + constexpr int kMaxNumRows = 3; + for (int num_rows = kMinNumRows; num_rows <= kMaxNumRows; ++num_rows) { + Matrix plus_jacobian = Matrix::Random(ambient_size, tangent_size); + EXPECT_TRUE(arg.PlusJacobian(x.data(), plus_jacobian.data())); + + Matrix ambient_matrix = Matrix::Random(num_rows, ambient_size); + Matrix expected = ambient_matrix * plus_jacobian; + + Matrix actual = Matrix::Random(num_rows, tangent_size); + EXPECT_TRUE(arg.RightMultiplyByPlusJacobian( + x.data(), num_rows, ambient_matrix.data(), actual.data())); + const double n = (actual - expected).norm(); + const double d = expected.norm(); + const double diffnorm = (d == 0.0) ? n : (n / d); + if (diffnorm > tolerance) { + *result_listener << "\nx: " << x.transpose() << "\nambient_matrix : \n" + << ambient_matrix << "\nplus_jacobian : \n" + << plus_jacobian << "\nexpected: \n" + << expected << "\nactual:\n" + << actual << "\ndiff:\n" + << expected - actual << "\ndiffnorm : " << diffnorm; + return false; + } + } + return true; +} + +#define EXPECT_THAT_MANIFOLD_INVARIANTS_HOLD(manifold, x, delta, y, tolerance) \ + Vector zero_tangent = Vector::Zero(manifold.TangentSize()); \ + EXPECT_THAT(manifold, XPlusZeroIsXAt(x, tolerance)); \ + EXPECT_THAT(manifold, XMinusXIsZeroAt(x, tolerance)); \ + EXPECT_THAT(manifold, MinusPlusIsIdentityAt(x, delta, tolerance)); \ + EXPECT_THAT(manifold, MinusPlusIsIdentityAt(x, zero_tangent, tolerance)); \ + EXPECT_THAT(manifold, PlusMinusIsIdentityAt(x, x, tolerance)); \ + EXPECT_THAT(manifold, PlusMinusIsIdentityAt(x, y, tolerance)); \ + EXPECT_THAT(manifold, HasCorrectPlusJacobianAt(x, tolerance)); \ + EXPECT_THAT(manifold, HasCorrectMinusJacobianAt(x, tolerance)); \ + EXPECT_THAT(manifold, MinusPlusJacobianIsIdentityAt(x, tolerance)); \ + EXPECT_THAT(manifold, HasCorrectRightMultiplyByPlusJacobianAt(x, tolerance)); + +} // namespace ceres diff --git a/extern/ceres/include/ceres/normal_prior.h b/extern/ceres/include/ceres/normal_prior.h index 14ab379f4af..c5c7f3e623e 100644 --- a/extern/ceres/include/ceres/normal_prior.h +++ b/extern/ceres/include/ceres/normal_prior.h @@ -57,7 +57,7 @@ namespace ceres { // which would be the case if the covariance matrix S is rank // deficient. -class CERES_EXPORT NormalPrior : public CostFunction { +class CERES_EXPORT NormalPrior final : public CostFunction { public: // Check that the number of rows in the vector b are the same as the // number of columns in the matrix A, crash otherwise. diff --git a/extern/ceres/include/ceres/numeric_diff_cost_function.h b/extern/ceres/include/ceres/numeric_diff_cost_function.h index cf7971cde79..6ec53175030 100644 --- a/extern/ceres/include/ceres/numeric_diff_cost_function.h +++ b/extern/ceres/include/ceres/numeric_diff_cost_function.h @@ -179,9 +179,10 @@ template <typename CostFunctor, NumericDiffMethodType method = CENTRAL, int kNumResiduals = 0, // Number of residuals, or ceres::DYNAMIC int... Ns> // Parameters dimensions for each block. -class NumericDiffCostFunction : public SizedCostFunction<kNumResiduals, Ns...> { +class NumericDiffCostFunction final + : public SizedCostFunction<kNumResiduals, Ns...> { public: - NumericDiffCostFunction( + explicit NumericDiffCostFunction( CostFunctor* functor, Ownership ownership = TAKE_OWNERSHIP, int num_residuals = kNumResiduals, @@ -192,7 +193,7 @@ class NumericDiffCostFunction : public SizedCostFunction<kNumResiduals, Ns...> { } } - explicit NumericDiffCostFunction(NumericDiffCostFunction&& other) + NumericDiffCostFunction(NumericDiffCostFunction&& other) : functor_(std::move(other.functor_)), ownership_(other.ownership_) {} virtual ~NumericDiffCostFunction() { @@ -219,7 +220,7 @@ class NumericDiffCostFunction : public SizedCostFunction<kNumResiduals, Ns...> { return false; } - if (jacobians == NULL) { + if (jacobians == nullptr) { return true; } @@ -246,6 +247,8 @@ class NumericDiffCostFunction : public SizedCostFunction<kNumResiduals, Ns...> { return true; } + const CostFunctor& functor() const { return *functor_; } + private: std::unique_ptr<CostFunctor> functor_; Ownership ownership_; diff --git a/extern/ceres/include/ceres/numeric_diff_first_order_function.h b/extern/ceres/include/ceres/numeric_diff_first_order_function.h new file mode 100644 index 00000000000..f5bb005be58 --- /dev/null +++ b/extern/ceres/include/ceres/numeric_diff_first_order_function.h @@ -0,0 +1,163 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2019 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#ifndef CERES_PUBLIC_NUMERIC_DIFF_FIRST_ORDER_FUNCTION_H_ +#define CERES_PUBLIC_NUMERIC_DIFF_FIRST_ORDER_FUNCTION_H_ + +#include <algorithm> +#include <memory> + +#include "ceres/first_order_function.h" +#include "ceres/internal/eigen.h" +#include "ceres/internal/fixed_array.h" +#include "ceres/internal/numeric_diff.h" +#include "ceres/internal/parameter_dims.h" +#include "ceres/internal/variadic_evaluate.h" +#include "ceres/numeric_diff_options.h" +#include "ceres/types.h" + +namespace ceres { + +// Creates FirstOrderFunctions as needed by the GradientProblem +// framework, with gradients computed via numeric differentiation. For +// more information on numeric differentiation, see the wikipedia +// article at https://en.wikipedia.org/wiki/Numerical_differentiation +// +// To get an numerically differentiated cost function, you must define +// a class with an operator() (a functor) that computes the cost. +// +// The function must write the computed value in the last argument +// (the only non-const one) and return true to indicate success. +// +// For example, consider a scalar error e = x'y - a, where both x and y are +// two-dimensional column vector parameters, the prime sign indicates +// transposition, and a is a constant. +// +// To write an numerically-differentiable cost function for the above model, +// first define the object +// +// class QuadraticCostFunctor { +// public: +// explicit QuadraticCostFunctor(double a) : a_(a) {} +// bool operator()(const double* const xy, double* cost) const { +// constexpr int kInputVectorLength = 2; +// const double* const x = xy; +// const double* const y = xy + kInputVectorLength; +// *cost = x[0] * y[0] + x[1] * y[1] - a_; +// return true; +// } +// +// private: +// double a_; +// }; +// +// +// Note that in the declaration of operator() the input parameters xy +// come first, and are passed as const pointers to array of +// doubles. The output cost is the last parameter. +// +// Then given this class definition, the numerically differentiated +// first order function with central differences used for computing the +// derivative can be constructed as follows. +// +// FirstOrderFunction* function +// = new NumericDiffFirstOrderFunction<MyScalarCostFunctor, CENTRAL, 4>( +// new QuadraticCostFunctor(1.0)); ^ ^ ^ +// | | | +// Finite Differencing Scheme -+ | | +// Dimension of xy ------------------------+ +// +// +// In the instantiation above, the template parameters following +// "QuadraticCostFunctor", "CENTRAL, 4", describe the finite +// differencing scheme as "central differencing" and the functor as +// computing its cost from a 4 dimensional input. +template <typename FirstOrderFunctor, + NumericDiffMethodType method, + int kNumParameters> +class NumericDiffFirstOrderFunction final : public FirstOrderFunction { + public: + explicit NumericDiffFirstOrderFunction( + FirstOrderFunctor* functor, + Ownership ownership = TAKE_OWNERSHIP, + const NumericDiffOptions& options = NumericDiffOptions()) + : functor_(functor), ownership_(ownership), options_(options) { + static_assert(kNumParameters > 0, "kNumParameters must be positive"); + } + + ~NumericDiffFirstOrderFunction() override { + if (ownership_ != TAKE_OWNERSHIP) { + functor_.release(); + } + } + + bool Evaluate(const double* const parameters, + double* cost, + double* gradient) const override { + using ParameterDims = internal::StaticParameterDims<kNumParameters>; + constexpr int kNumResiduals = 1; + + // Get the function value (cost) at the the point to evaluate. + if (!internal::VariadicEvaluate<ParameterDims>( + *functor_, ¶meters, cost)) { + return false; + } + + if (gradient == nullptr) { + return true; + } + + // Create a copy of the parameters which will get mutated. + internal::FixedArray<double, 32> parameters_copy(kNumParameters); + std::copy_n(parameters, kNumParameters, parameters_copy.data()); + double* parameters_ptr = parameters_copy.data(); + internal::EvaluateJacobianForParameterBlocks< + ParameterDims>::template Apply<method, kNumResiduals>(functor_.get(), + cost, + options_, + kNumResiduals, + ¶meters_ptr, + &gradient); + return true; + } + + int NumParameters() const override { return kNumParameters; } + + const FirstOrderFunctor& functor() const { return *functor_; } + + private: + std::unique_ptr<FirstOrderFunctor> functor_; + Ownership ownership_; + NumericDiffOptions options_; +}; + +} // namespace ceres + +#endif // CERES_PUBLIC_NUMERIC_DIFF_FIRST_ORDER_FUNCTION_H_ diff --git a/extern/ceres/include/ceres/numeric_diff_options.h b/extern/ceres/include/ceres/numeric_diff_options.h index 64919ed5ab1..b025b51d938 100644 --- a/extern/ceres/include/ceres/numeric_diff_options.h +++ b/extern/ceres/include/ceres/numeric_diff_options.h @@ -32,7 +32,8 @@ #ifndef CERES_PUBLIC_NUMERIC_DIFF_OPTIONS_H_ #define CERES_PUBLIC_NUMERIC_DIFF_OPTIONS_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { @@ -70,4 +71,6 @@ struct CERES_EXPORT NumericDiffOptions { } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_PUBLIC_NUMERIC_DIFF_OPTIONS_H_ diff --git a/extern/ceres/include/ceres/ordered_groups.h b/extern/ceres/include/ceres/ordered_groups.h index 954663c97e6..c1531cce65f 100644 --- a/extern/ceres/include/ceres/ordered_groups.h +++ b/extern/ceres/include/ceres/ordered_groups.h @@ -36,7 +36,7 @@ #include <unordered_map> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "glog/logging.h" namespace ceres { @@ -190,7 +190,7 @@ class OrderedGroups { }; // Typedef for the most commonly used version of OrderedGroups. -typedef OrderedGroups<double*> ParameterBlockOrdering; +using ParameterBlockOrdering = OrderedGroups<double*>; } // namespace ceres diff --git a/extern/ceres/include/ceres/problem.h b/extern/ceres/include/ceres/problem.h index add12ea401d..819fa454b21 100644 --- a/extern/ceres/include/ceres/problem.h +++ b/extern/ceres/include/ceres/problem.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2021 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -43,6 +43,7 @@ #include "ceres/context.h" #include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/internal/port.h" #include "ceres/types.h" #include "glog/logging.h" @@ -53,6 +54,7 @@ class CostFunction; class EvaluationCallback; class LossFunction; class LocalParameterization; +class Manifold; class Solver; struct CRSMatrix; @@ -65,7 +67,7 @@ class ResidualBlock; // A ResidualBlockId is an opaque handle clients can use to remove residual // blocks from a Problem after adding them. -typedef internal::ResidualBlock* ResidualBlockId; +using ResidualBlockId = internal::ResidualBlock*; // A class to represent non-linear least squares problems. Such // problems have a cost function that is a sum of error terms (known @@ -78,31 +80,28 @@ typedef internal::ResidualBlock* ResidualBlockId; // // where // -// r_ij is residual number i, component j; the residual is a -// function of some subset of the parameters x1...xk. For -// example, in a structure from motion problem a residual -// might be the difference between a measured point in an -// image and the reprojected position for the matching -// camera, point pair. The residual would have two -// components, error in x and error in y. +// r_ij is residual number i, component j; the residual is a function of some +// subset of the parameters x1...xk. For example, in a structure from +// motion problem a residual might be the difference between a measured +// point in an image and the reprojected position for the matching +// camera, point pair. The residual would have two components, error in x +// and error in y. // -// loss(y) is the loss function; for example, squared error or -// Huber L1 loss. If loss(y) = y, then the cost function is -// non-robustified least squares. +// loss(y) is the loss function; for example, squared error or Huber L1 +// loss. If loss(y) = y, then the cost function is non-robustified +// least squares. // -// This class is specifically designed to address the important subset -// of "sparse" least squares problems, where each component of the -// residual depends only on a small number number of parameters, even -// though the total number of residuals and parameters may be very -// large. This property affords tremendous gains in scale, allowing -// efficient solving of large problems that are otherwise -// inaccessible. +// This class is specifically designed to address the important subset of +// "sparse" least squares problems, where each component of the residual depends +// only on a small number number of parameters, even though the total number of +// residuals and parameters may be very large. This property affords tremendous +// gains in scale, allowing efficient solving of large problems that are +// otherwise inaccessible. // // The canonical example of a sparse least squares problem is -// "structure-from-motion" (SFM), where the parameters are points and -// cameras, and residuals are reprojection errors. Typically a single -// residual will depend only on 9 parameters (3 for the point, 6 for -// the camera). +// "structure-from-motion" (SFM), where the parameters are points and cameras, +// and residuals are reprojection errors. Typically a single residual will +// depend only on 9 parameters (3 for the point, 6 for the camera). // // To create a least squares problem, use the AddResidualBlock() and // AddParameterBlock() methods, documented below. Here is an example least @@ -119,41 +118,52 @@ typedef internal::ResidualBlock* ResidualBlockId; // problem.AddResidualBlock(new MyBinaryCostFunction(...), nullptr, x2, x3); // // Please see cost_function.h for details of the CostFunction object. +// +// NOTE: We are currently in the process of transitioning from +// LocalParameterization to Manifolds in the Ceres API. During this period, +// Problem will support using both Manifold and LocalParameterization objects +// interchangably. In particular, adding a LocalParameterization to a parameter +// block is the same as adding a Manifold to that parameter block. For methods +// in the API affected by this change, see their documentation below. class CERES_EXPORT Problem { public: struct CERES_EXPORT Options { - // These flags control whether the Problem object owns the cost - // functions, loss functions, and parameterizations passed into - // the Problem. If set to TAKE_OWNERSHIP, then the problem object - // will delete the corresponding cost or loss functions on - // destruction. The destructor is careful to delete the pointers - // only once, since sharing cost/loss/parameterizations is - // allowed. + // These flags control whether the Problem object owns the CostFunctions, + // LossFunctions, LocalParameterizations, and Manifolds passed into the + // Problem. + // + // If set to TAKE_OWNERSHIP, then the problem object will delete the + // corresponding object on destruction. The destructor is careful to delete + // the pointers only once, since sharing objects is allowed. Ownership cost_function_ownership = TAKE_OWNERSHIP; Ownership loss_function_ownership = TAKE_OWNERSHIP; + CERES_DEPRECATED_WITH_MSG( + "Local Parameterizations are deprecated. Use Manifold and " + "manifold_ownership instead.") Ownership local_parameterization_ownership = TAKE_OWNERSHIP; + Ownership manifold_ownership = TAKE_OWNERSHIP; // If true, trades memory for faster RemoveResidualBlock() and // RemoveParameterBlock() operations. // // By default, RemoveParameterBlock() and RemoveResidualBlock() take time - // proportional to the size of the entire problem. If you only ever remove + // proportional to the size of the entire problem. If you only ever remove // parameters or residuals from the problem occasionally, this might be - // acceptable. However, if you have memory to spare, enable this option to + // acceptable. However, if you have memory to spare, enable this option to // make RemoveParameterBlock() take time proportional to the number of // residual blocks that depend on it, and RemoveResidualBlock() take (on // average) constant time. // - // The increase in memory usage is twofold: an additional hash set per + // The increase in memory usage is two-fold: an additional hash set per // parameter block containing all the residuals that depend on the parameter // block; and a hash set in the problem containing all residuals. bool enable_fast_removal = false; // By default, Ceres performs a variety of safety checks when constructing - // the problem. There is a small but measurable performance penalty to - // these checks, typically around 5% of construction time. If you are sure - // your problem construction is correct, and 5% of the problem construction - // time is truly an overhead you want to avoid, then you can set + // the problem. There is a small but measurable performance penalty to these + // checks, typically around 5% of construction time. If you are sure your + // problem construction is correct, and 5% of the problem construction time + // is truly an overhead you want to avoid, then you can set // disable_all_safety_checks to true. // // WARNING: Do not set this to true, unless you are absolutely sure of what @@ -167,26 +177,23 @@ class CERES_EXPORT Problem { // Ceres does NOT take ownership of the pointer. Context* context = nullptr; - // Using this callback interface, Ceres can notify you when it is - // about to evaluate the residuals or jacobians. With the - // callback, you can share computation between residual blocks by - // doing the shared computation in + // Using this callback interface, Ceres can notify you when it is about to + // evaluate the residuals or jacobians. With the callback, you can share + // computation between residual blocks by doing the shared computation in // EvaluationCallback::PrepareForEvaluation() before Ceres calls - // CostFunction::Evaluate(). It also enables caching results - // between a pure residual evaluation and a residual & jacobian - // evaluation. + // CostFunction::Evaluate(). It also enables caching results between a pure + // residual evaluation and a residual & jacobian evaluation. // // Problem DOES NOT take ownership of the callback. // - // NOTE: Evaluation callbacks are incompatible with inner - // iterations. So calling Solve with - // Solver::Options::use_inner_iterations = true on a Problem with - // a non-null evaluation callback is an error. + // NOTE: Evaluation callbacks are incompatible with inner iterations. So + // calling Solve with Solver::Options::use_inner_iterations = true on a + // Problem with a non-null evaluation callback is an error. EvaluationCallback* evaluation_callback = nullptr; }; - // The default constructor is equivalent to the - // invocation Problem(Problem::Options()). + // The default constructor is equivalent to the invocation + // Problem(Problem::Options()). Problem(); explicit Problem(const Options& options); Problem(Problem&&); @@ -197,31 +204,29 @@ class CERES_EXPORT Problem { ~Problem(); - // Add a residual block to the overall cost function. The cost - // function carries with its information about the sizes of the - // parameter blocks it expects. The function checks that these match - // the sizes of the parameter blocks listed in parameter_blocks. The - // program aborts if a mismatch is detected. loss_function can be - // nullptr, in which case the cost of the term is just the squared norm - // of the residuals. - // - // The user has the option of explicitly adding the parameter blocks - // using AddParameterBlock. This causes additional correctness - // checking; however, AddResidualBlock implicitly adds the parameter - // blocks if they are not present, so calling AddParameterBlock - // explicitly is not required. - // - // The Problem object by default takes ownership of the - // cost_function and loss_function pointers. These objects remain - // live for the life of the Problem object. If the user wishes to - // keep control over the destruction of these objects, then they can + // Add a residual block to the overall cost function. The cost function + // carries with its information about the sizes of the parameter blocks it + // expects. The function checks that these match the sizes of the parameter + // blocks listed in parameter_blocks. The program aborts if a mismatch is + // detected. loss_function can be nullptr, in which case the cost of the term + // is just the squared norm of the residuals. + // + // The user has the option of explicitly adding the parameter blocks using + // AddParameterBlock. This causes additional correctness checking; however, + // AddResidualBlock implicitly adds the parameter blocks if they are not + // present, so calling AddParameterBlock explicitly is not required. + // + // The Problem object by default takes ownership of the cost_function and + // loss_function pointers (See Problem::Options to override this behaviour). + // These objects remain live for the life of the Problem object. If the user + // wishes to keep control over the destruction of these objects, then they can // do this by setting the corresponding enums in the Options struct. // - // Note: Even though the Problem takes ownership of cost_function - // and loss_function, it does not preclude the user from re-using - // them in another residual block. The destructor takes care to call - // delete on each cost_function or loss_function pointer only once, - // regardless of how many residual blocks refer to them. + // Note: Even though the Problem takes ownership of cost_function and + // loss_function, it does not preclude the user from re-using them in another + // residual block. The destructor takes care to call delete on each + // cost_function or loss_function pointer only once, regardless of how many + // residual blocks refer to them. // // Example usage: // @@ -234,8 +239,8 @@ class CERES_EXPORT Problem { // problem.AddResidualBlock(new MyUnaryCostFunction(...), nullptr, x1); // problem.AddResidualBlock(new MyBinaryCostFunction(...), nullptr, x2, x1); // - // Add a residual block by listing the parameter block pointers - // directly instead of wapping them in a container. + // Add a residual block by listing the parameter block pointers directly + // instead of wapping them in a container. template <typename... Ts> ResidualBlockId AddResidualBlock(CostFunction* cost_function, LossFunction* loss_function, @@ -261,29 +266,75 @@ class CERES_EXPORT Problem { double* const* const parameter_blocks, int num_parameter_blocks); - // Add a parameter block with appropriate size to the problem. - // Repeated calls with the same arguments are ignored. Repeated - // calls with the same double pointer but a different size results - // in undefined behaviour. + // Add a parameter block with appropriate size to the problem. Repeated calls + // with the same arguments are ignored. Repeated calls with the same double + // pointer but a different size will result in a crash. void AddParameterBlock(double* values, int size); - // Add a parameter block with appropriate size and parameterization - // to the problem. Repeated calls with the same arguments are - // ignored. Repeated calls with the same double pointer but a - // different size results in undefined behaviour. + // Add a parameter block with appropriate size and parameterization to the + // problem. It is okay for local_parameterization to be nullptr. + // + // Repeated calls with the same arguments are ignored. Repeated calls + // with the same double pointer but a different size results in a crash + // (unless Solver::Options::diable_all_safety_checks is set to true). + // + // Repeated calls with the same double pointer and size but different + // LocalParameterization is equivalent to calling + // SetParameterization(local_parameterization), i.e., any previously + // associated LocalParameterization or Manifold object will be replaced with + // the local_parameterization. + // + // NOTE: + // ---- + // + // This method is deprecated and will be removed in the next public + // release of Ceres Solver. Please move to using the Manifold based version of + // AddParameterBlock. + // + // During the transition from LocalParameterization to Manifold, internally + // the LocalParameterization is treated as a Manifold by wrapping it using a + // ManifoldAdapter object. So HasManifold() will return true, GetManifold() + // will return the wrapped object and ParameterBlockTangentSize() will return + // the LocalSize of the LocalParameterization. + CERES_DEPRECATED_WITH_MSG( + "LocalParameterizations are deprecated. Use the version with Manifolds " + "instead.") void AddParameterBlock(double* values, int size, LocalParameterization* local_parameterization); - // Remove a parameter block from the problem. The parameterization of the - // parameter block, if it exists, will persist until the deletion of the - // problem (similar to cost/loss functions in residual block removal). Any - // residual blocks that depend on the parameter are also removed, as - // described above in RemoveResidualBlock(). + // Add a parameter block with appropriate size and Manifold to the + // problem. It is okay for manifold to be nullptr. // - // If Problem::Options::enable_fast_removal is true, then the - // removal is fast (almost constant time). Otherwise, removing a parameter - // block will incur a scan of the entire Problem object. + // Repeated calls with the same arguments are ignored. Repeated calls + // with the same double pointer but a different size results in a crash + // (unless Solver::Options::diable_all_safety_checks is set to true). + // + // Repeated calls with the same double pointer and size but different Manifold + // is equivalent to calling SetManifold(manifold), i.e., any previously + // associated LocalParameterization or Manifold object will be replaced with + // the manifold. + // + // Note: + // ---- + // + // During the transition from LocalParameterization to Manifold, calling + // AddParameterBlock with a Manifold when a LocalParameterization is already + // associated with the parameter block is okay. It is equivalent to calling + // SetManifold(manifold), i.e., any previously associated + // LocalParameterization or Manifold object will be replaced with the + // manifold. + void AddParameterBlock(double* values, int size, Manifold* manifold); + + // Remove a parameter block from the problem. The LocalParameterization or + // Manifold of the parameter block, if it exists, will persist until the + // deletion of the problem (similar to cost/loss functions in residual block + // removal). Any residual blocks that depend on the parameter are also + // removed, as described above in RemoveResidualBlock(). + // + // If Problem::Options::enable_fast_removal is true, then the removal is fast + // (almost constant time). Otherwise, removing a parameter block will incur a + // scan of the entire Problem object. // // WARNING: Removing a residual or parameter block will destroy the implicit // ordering, rendering the jacobian or residuals returned from the solver @@ -308,35 +359,109 @@ class CERES_EXPORT Problem { // Allow the indicated parameter block to vary during optimization. void SetParameterBlockVariable(double* values); - // Returns true if a parameter block is set constant, and false - // otherwise. A parameter block may be set constant in two ways: - // either by calling SetParameterBlockConstant or by associating a - // LocalParameterization with a zero dimensional tangent space with - // it. + // Returns true if a parameter block is set constant, and false otherwise. A + // parameter block may be set constant in two ways: either by calling + // SetParameterBlockConstant or by associating a LocalParameterization or + // Manifold with a zero dimensional tangent space with it. bool IsParameterBlockConstant(const double* values) const; - // Set the local parameterization for one of the parameter blocks. - // The local_parameterization is owned by the Problem by default. It - // is acceptable to set the same parameterization for multiple - // parameters; the destructor is careful to delete local - // parameterizations only once. Calling SetParameterization with - // nullptr will clear any previously set parameterization. + // Set the LocalParameterization for the parameter block. Calling + // SetParameterization with nullptr will clear any previously set + // LocalParameterization or Manifold for the parameter block. + // + // Repeated calls will cause any previously associated LocalParameterization + // or Manifold object to be replaced with the local_parameterization. + // + // The local_parameterization is owned by the Problem by default (See + // Problem::Options to override this behaviour). + // + // It is acceptable to set the same LocalParameterization for multiple + // parameter blocks; the destructor is careful to delete + // LocalParamaterizations only once. + // + // NOTE: + // ---- + // + // This method is deprecated and will be removed in the next public + // release of Ceres Solver. Please move to using the SetManifold instead. + // + // During the transition from LocalParameterization to Manifold, internally + // the LocalParameterization is treated as a Manifold by wrapping it using a + // ManifoldAdapter object. So HasManifold() will return true, GetManifold() + // will return the wrapped object and ParameterBlockTangentSize will return + // the same value of ParameterBlockLocalSize. + CERES_DEPRECATED_WITH_MSG( + "LocalParameterizations are deprecated. Use SetManifold instead.") void SetParameterization(double* values, LocalParameterization* local_parameterization); - // Get the local parameterization object associated with this - // parameter block. If there is no parameterization object - // associated then nullptr is returned. + // Get the LocalParameterization object associated with this parameter block. + // If there is no LocalParameterization associated then nullptr is returned. + // + // NOTE: This method is deprecated and will be removed in the next public + // release of Ceres Solver. Use GetManifold instead. + // + // Note also that if a LocalParameterization is associated with a parameter + // block, HasManifold will return true and GetManifold will return the + // LocalParameterization wrapped in a ManifoldAdapter. + // + // The converse is NOT true, i.e., if a Manifold is associated with a + // parameter block, HasParameterization will return false and + // GetParameterization will return a nullptr. + CERES_DEPRECATED_WITH_MSG( + "LocalParameterizations are deprecated. Use GetManifold " + "instead.") const LocalParameterization* GetParameterization(const double* values) const; + // Returns true if a LocalParameterization is associated with this parameter + // block, false otherwise. + // + // NOTE: This method is deprecated and will be removed in the next public + // release of Ceres Solver. Use HasManifold instead. + // + // Note also that if a Manifold is associated with the parameter block, this + // method will return false. + CERES_DEPRECATED_WITH_MSG( + "LocalParameterizations are deprecated. Use HasManifold instead.") + bool HasParameterization(const double* values) const; + + // Set the Manifold for the parameter block. Calling SetManifold with nullptr + // will clear any previously set LocalParameterization or Manifold for the + // parameter block. + // + // Repeated calls will result in any previously associated + // LocalParameterization or Manifold object to be replaced with the manifold. + // + // The manifold is owned by the Problem by default (See Problem::Options to + // override this behaviour). + // + // It is acceptable to set the same Manifold for multiple parameter blocks. + void SetManifold(double* values, Manifold* manifold); + + // Get the Manifold object associated with this parameter block. + // + // If there is no Manifold Or LocalParameterization object associated then + // nullptr is returned. + // + // NOTE: During the transition from LocalParameterization to Manifold, + // internally the LocalParameterization is treated as a Manifold by wrapping + // it using a ManifoldAdapter object. So calling GetManifold on a parameter + // block with a LocalParameterization associated with it will return the + // LocalParameterization wrapped in a ManifoldAdapter + const Manifold* GetManifold(const double* values) const; + + // Returns true if a Manifold or a LocalParameterization is associated with + // this parameter block, false otherwise. + bool HasManifold(const double* values) const; + // Set the lower/upper bound for the parameter at position "index". void SetParameterLowerBound(double* values, int index, double lower_bound); void SetParameterUpperBound(double* values, int index, double upper_bound); - // Get the lower/upper bound for the parameter at position - // "index". If the parameter is not bounded by the user, then its - // lower bound is -std::numeric_limits<double>::max() and upper - // bound is std::numeric_limits<double>::max(). + // Get the lower/upper bound for the parameter at position "index". If the + // parameter is not bounded by the user, then its lower bound is + // -std::numeric_limits<double>::max() and upper bound is + // std::numeric_limits<double>::max(). double GetParameterLowerBound(const double* values, int index) const; double GetParameterUpperBound(const double* values, int index) const; @@ -344,37 +469,47 @@ class CERES_EXPORT Problem { // parameter_blocks().size() and parameter_block_sizes().size(). int NumParameterBlocks() const; - // The size of the parameter vector obtained by summing over the - // sizes of all the parameter blocks. + // The size of the parameter vector obtained by summing over the sizes of all + // the parameter blocks. int NumParameters() const; // Number of residual blocks in the problem. Always equals // residual_blocks().size(). int NumResidualBlocks() const; - // The size of the residual vector obtained by summing over the - // sizes of all of the residual blocks. + // The size of the residual vector obtained by summing over the sizes of all + // of the residual blocks. int NumResiduals() const; // The size of the parameter block. int ParameterBlockSize(const double* values) const; - // The size of local parameterization for the parameter block. If - // there is no local parameterization associated with this parameter - // block, then ParameterBlockLocalSize = ParameterBlockSize. + // The dimension of the tangent space of the LocalParameterization or Manifold + // for the parameter block. If there is no LocalParameterization or Manifold + // associated with this parameter block, then ParameterBlockLocalSize = + // ParameterBlockSize. + CERES_DEPRECATED_WITH_MSG( + "LocalParameterizations are deprecated. Use ParameterBlockTangentSize " + "instead.") int ParameterBlockLocalSize(const double* values) const; + // The dimenion of the tangent space of the LocalParameterization or Manifold + // for the parameter block. If there is no LocalParameterization or Manifold + // associated with this parameter block, then ParameterBlockTangentSize = + // ParameterBlockSize. + int ParameterBlockTangentSize(const double* values) const; + // Is the given parameter block present in this problem or not? bool HasParameterBlock(const double* values) const; - // Fills the passed parameter_blocks vector with pointers to the - // parameter blocks currently in the problem. After this call, - // parameter_block.size() == NumParameterBlocks. + // Fills the passed parameter_blocks vector with pointers to the parameter + // blocks currently in the problem. After this call, parameter_block.size() == + // NumParameterBlocks. void GetParameterBlocks(std::vector<double*>* parameter_blocks) const; - // Fills the passed residual_blocks vector with pointers to the - // residual blocks currently in the problem. After this call, - // residual_blocks.size() == NumResidualBlocks. + // Fills the passed residual_blocks vector with pointers to the residual + // blocks currently in the problem. After this call, residual_blocks.size() == + // NumResidualBlocks. void GetResidualBlocks(std::vector<ResidualBlockId>* residual_blocks) const; // Get all the parameter blocks that depend on the given residual block. @@ -393,10 +528,10 @@ class CERES_EXPORT Problem { // Get all the residual blocks that depend on the given parameter block. // - // If Problem::Options::enable_fast_removal is true, then - // getting the residual blocks is fast and depends only on the number of - // residual blocks. Otherwise, getting the residual blocks for a parameter - // block will incur a scan of the entire Problem object. + // If Problem::Options::enable_fast_removal is true, then getting the residual + // blocks is fast and depends only on the number of residual + // blocks. Otherwise, getting the residual blocks for a parameter block will + // incur a scan of the entire Problem object. void GetResidualBlocksForParameterBlock( const double* values, std::vector<ResidualBlockId>* residual_blocks) const; @@ -404,49 +539,45 @@ class CERES_EXPORT Problem { // Options struct to control Problem::Evaluate. struct EvaluateOptions { // The set of parameter blocks for which evaluation should be - // performed. This vector determines the order that parameter - // blocks occur in the gradient vector and in the columns of the - // jacobian matrix. If parameter_blocks is empty, then it is - // assumed to be equal to vector containing ALL the parameter - // blocks. Generally speaking the parameter blocks will occur in - // the order in which they were added to the problem. But, this - // may change if the user removes any parameter blocks from the - // problem. + // performed. This vector determines the order that parameter blocks occur + // in the gradient vector and in the columns of the jacobian matrix. If + // parameter_blocks is empty, then it is assumed to be equal to vector + // containing ALL the parameter blocks. Generally speaking the parameter + // blocks will occur in the order in which they were added to the + // problem. But, this may change if the user removes any parameter blocks + // from the problem. // - // NOTE: This vector should contain the same pointers as the ones - // used to add parameter blocks to the Problem. These parameter - // block should NOT point to new memory locations. Bad things will - // happen otherwise. + // NOTE: This vector should contain the same pointers as the ones used to + // add parameter blocks to the Problem. These parameter block should NOT + // point to new memory locations. Bad things will happen otherwise. std::vector<double*> parameter_blocks; - // The set of residual blocks to evaluate. This vector determines - // the order in which the residuals occur, and how the rows of the - // jacobian are ordered. If residual_blocks is empty, then it is - // assumed to be equal to the vector containing ALL the residual - // blocks. Generally speaking the residual blocks will occur in - // the order in which they were added to the problem. But, this - // may change if the user removes any residual blocks from the - // problem. + // The set of residual blocks to evaluate. This vector determines the order + // in which the residuals occur, and how the rows of the jacobian are + // ordered. If residual_blocks is empty, then it is assumed to be equal to + // the vector containing ALL the residual blocks. Generally speaking the + // residual blocks will occur in the order in which they were added to the + // problem. But, this may change if the user removes any residual blocks + // from the problem. std::vector<ResidualBlockId> residual_blocks; // Even though the residual blocks in the problem may contain loss - // functions, setting apply_loss_function to false will turn off - // the application of the loss function to the output of the cost - // function. This is of use for example if the user wishes to - // analyse the solution quality by studying the distribution of - // residuals before and after the solve. + // functions, setting apply_loss_function to false will turn off the + // application of the loss function to the output of the cost function. This + // is of use for example if the user wishes to analyse the solution quality + // by studying the distribution of residuals before and after the solve. bool apply_loss_function = true; int num_threads = 1; }; - // Evaluate Problem. Any of the output pointers can be nullptr. Which - // residual blocks and parameter blocks are used is controlled by - // the EvaluateOptions struct above. + // Evaluate Problem. Any of the output pointers can be nullptr. Which residual + // blocks and parameter blocks are used is controlled by the EvaluateOptions + // struct above. // - // Note 1: The evaluation will use the values stored in the memory - // locations pointed to by the parameter block pointers used at the - // time of the construction of the problem. i.e., + // Note 1: The evaluation will use the values stored in the memory locations + // pointed to by the parameter block pointers used at the time of the + // construction of the problem. i.e., // // Problem problem; // double x = 1; @@ -456,8 +587,8 @@ class CERES_EXPORT Problem { // problem.Evaluate(Problem::EvaluateOptions(), &cost, // nullptr, nullptr, nullptr); // - // The cost is evaluated at x = 1. If you wish to evaluate the - // problem at x = 2, then + // The cost is evaluated at x = 1. If you wish to evaluate the problem at x = + // 2, then // // x = 2; // problem.Evaluate(Problem::EvaluateOptions(), &cost, @@ -465,80 +596,75 @@ class CERES_EXPORT Problem { // // is the way to do so. // - // Note 2: If no local parameterizations are used, then the size of - // the gradient vector (and the number of columns in the jacobian) - // is the sum of the sizes of all the parameter blocks. If a - // parameter block has a local parameterization, then it contributes - // "LocalSize" entries to the gradient vector (and the number of - // columns in the jacobian). + // Note 2: If no LocalParameterizations or Manifolds are used, then the size + // of the gradient vector (and the number of columns in the jacobian) is the + // sum of the sizes of all the parameter blocks. If a parameter block has a + // LocalParameterization or Manifold, then it contributes "TangentSize" + // entries to the gradient vector (and the number of columns in the jacobian). // - // Note 3: This function cannot be called while the problem is being - // solved, for example it cannot be called from an IterationCallback - // at the end of an iteration during a solve. + // Note 3: This function cannot be called while the problem is being solved, + // for example it cannot be called from an IterationCallback at the end of an + // iteration during a solve. // - // Note 4: If an EvaluationCallback is associated with the problem, - // then its PrepareForEvaluation method will be called every time - // this method is called with new_point = true. + // Note 4: If an EvaluationCallback is associated with the problem, then its + // PrepareForEvaluation method will be called every time this method is called + // with new_point = true. bool Evaluate(const EvaluateOptions& options, double* cost, std::vector<double>* residuals, std::vector<double>* gradient, CRSMatrix* jacobian); - // Evaluates the residual block, storing the scalar cost in *cost, - // the residual components in *residuals, and the jacobians between - // the parameters and residuals in jacobians[i], in row-major order. + // Evaluates the residual block, storing the scalar cost in *cost, the + // residual components in *residuals, and the jacobians between the parameters + // and residuals in jacobians[i], in row-major order. // // If residuals is nullptr, the residuals are not computed. // - // If jacobians is nullptr, no Jacobians are computed. If - // jacobians[i] is nullptr, then the Jacobian for that parameter - // block is not computed. + // If jacobians is nullptr, no Jacobians are computed. If jacobians[i] is + // nullptr, then the Jacobian for that parameter block is not computed. // - // It is not okay to request the Jacobian w.r.t a parameter block - // that is constant. + // It is not okay to request the Jacobian w.r.t a parameter block that is + // constant. // - // The return value indicates the success or failure. Even if the - // function returns false, the caller should expect the output - // memory locations to have been modified. + // The return value indicates the success or failure. Even if the function + // returns false, the caller should expect the output memory locations to have + // been modified. // // The returned cost and jacobians have had robustification and - // local parameterizations applied already; for example, the - // jacobian for a 4-dimensional quaternion parameter using the - // "QuaternionParameterization" is num_residuals by 3 instead of - // num_residuals by 4. + // LocalParameterization/Manifold applied already; for example, the jacobian + // for a 4-dimensional quaternion parameter using the + // "QuaternionParameterization" is num_residuals by 3 instead of num_residuals + // by 4. // - // apply_loss_function as the name implies allows the user to switch - // the application of the loss function on and off. + // apply_loss_function as the name implies allows the user to switch the + // application of the loss function on and off. // // If an EvaluationCallback is associated with the problem, then its - // PrepareForEvaluation method will be called every time this method - // is called with new_point = true. This conservatively assumes that - // the user may have changed the parameter values since the previous - // call to evaluate / solve. For improved efficiency, and only if - // you know that the parameter values have not changed between - // calls, see EvaluateResidualBlockAssumingParametersUnchanged(). + // PrepareForEvaluation method will be called every time this method is called + // with new_point = true. This conservatively assumes that the user may have + // changed the parameter values since the previous call to evaluate / solve. + // For improved efficiency, and only if you know that the parameter values + // have not changed between calls, see + // EvaluateResidualBlockAssumingParametersUnchanged(). bool EvaluateResidualBlock(ResidualBlockId residual_block_id, bool apply_loss_function, double* cost, double* residuals, double** jacobians) const; - // Same as EvaluateResidualBlock except that if an - // EvaluationCallback is associated with the problem, then its - // PrepareForEvaluation method will be called every time this method - // is called with new_point = false. - // - // This means, if an EvaluationCallback is associated with the - // problem then it is the user's responsibility to call - // PrepareForEvaluation before calling this method if necessary, - // i.e. iff the parameter values have been changed since the last - // call to evaluate / solve.' - // - // This is because, as the name implies, we assume that the - // parameter blocks did not change since the last time - // PrepareForEvaluation was called (via Solve, Evaluate or - // EvaluateResidualBlock). + // Same as EvaluateResidualBlock except that if an EvaluationCallback is + // associated with the problem, then its PrepareForEvaluation method will be + // called every time this method is called with new_point = false. + // + // This means, if an EvaluationCallback is associated with the problem then it + // is the user's responsibility to call PrepareForEvaluation before calling + // this method if necessary, i.e. iff the parameter values have been changed + // since the last call to evaluate / solve.' + // + // This is because, as the name implies, we assume that the parameter blocks + // did not change since the last time PrepareForEvaluation was called (via + // Solve, Evaluate or EvaluateResidualBlock). bool EvaluateResidualBlockAssumingParametersUnchanged( ResidualBlockId residual_block_id, bool apply_loss_function, diff --git a/extern/ceres/include/ceres/product_manifold.h b/extern/ceres/include/ceres/product_manifold.h new file mode 100644 index 00000000000..33f046da24e --- /dev/null +++ b/extern/ceres/include/ceres/product_manifold.h @@ -0,0 +1,328 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) +// sergiu.deitsch@gmail.com (Sergiu Deitsch) +// + +#ifndef CERES_PUBLIC_PRODUCT_MANIFOLD_H_ +#define CERES_PUBLIC_PRODUCT_MANIFOLD_H_ + +#include <algorithm> +#include <array> +#include <cassert> +#include <cstddef> +#include <numeric> +#include <tuple> +#include <type_traits> +#include <utility> + +#include "ceres/internal/eigen.h" +#include "ceres/internal/fixed_array.h" +#include "ceres/internal/port.h" +#include "ceres/manifold.h" + +namespace ceres { + +// Construct a manifold by taking the Cartesian product of a number of other +// manifolds. This is useful, when a parameter block is the Cartesian product +// of two or more manifolds. For example the parameters of a camera consist of +// a rotation and a translation, i.e., SO(3) x R^3. +// +// Example usage: +// +// ProductManifold<QuaternionManifold, EuclideanManifold<3>> se3; +// +// is the manifold for a rigid transformation, where the rotation is +// represented using a quaternion. +// +// Manifolds can be copied and moved to ProductManifold: +// +// SubsetManifold manifold1(5, {2}); +// SubsetManifold manifold2(3, {0, 1}); +// ProductManifold<SubsetManifold, SubsetManifold> manifold(manifold1, +// manifold2); +// +// In advanced use cases, manifolds can be dynamically allocated and passed as +// (smart) pointers: +// +// ProductManifold<std::unique_ptr<QuaternionManifold>, EuclideanManifold<3>> +// se3{std::make_unique<QuaternionManifold>(), EuclideanManifold<3>{}}; +// +// In C++17, the template parameters can be left out as they are automatically +// deduced making the initialization much simpler: +// +// ProductManifold se3{QuaternionManifold{}, EuclideanManifold<3>{}}; +// +// The manifold implementations must be either default constructible, copyable +// or moveable to be usable in a ProductManifold. +template <typename Manifold0, typename Manifold1, typename... ManifoldN> +class ProductManifold final : public Manifold { + public: + // ProductManifold constructor perfect forwards arguments to store manifolds. + // + // Either use default construction or if you need to copy or move-construct a + // manifold instance, you need to pass an instance as an argument for all + // types given as class template parameters. + template <typename... Args, + std::enable_if_t<std::is_constructible< + std::tuple<Manifold0, Manifold1, ManifoldN...>, + Args...>::value>* = nullptr> + explicit ProductManifold(Args&&... manifolds) + : ProductManifold{std::make_index_sequence<kNumManifolds>{}, + std::forward<Args>(manifolds)...} {} + + int AmbientSize() const override { return ambient_size_; } + int TangentSize() const override { return tangent_size_; } + + bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const override { + return PlusImpl( + x, delta, x_plus_delta, std::make_index_sequence<kNumManifolds>{}); + } + + bool Minus(const double* y, + const double* x, + double* y_minus_x) const override { + return MinusImpl( + y, x, y_minus_x, std::make_index_sequence<kNumManifolds>{}); + } + + bool PlusJacobian(const double* x, double* jacobian_ptr) const override { + MatrixRef jacobian(jacobian_ptr, AmbientSize(), TangentSize()); + jacobian.setZero(); + internal::FixedArray<double> buffer(buffer_size_); + + return PlusJacobianImpl( + x, jacobian, buffer, std::make_index_sequence<kNumManifolds>{}); + } + + bool MinusJacobian(const double* x, double* jacobian_ptr) const override { + MatrixRef jacobian(jacobian_ptr, TangentSize(), AmbientSize()); + jacobian.setZero(); + internal::FixedArray<double> buffer(buffer_size_); + + return MinusJacobianImpl( + x, jacobian, buffer, std::make_index_sequence<kNumManifolds>{}); + } + + private: + static constexpr std::size_t kNumManifolds = 2 + sizeof...(ManifoldN); + + template <std::size_t... Indices, typename... Args> + explicit ProductManifold(std::index_sequence<Indices...>, Args&&... manifolds) + : manifolds_{std::forward<Args>(manifolds)...}, + buffer_size_{(std::max)( + {(Dereference(std::get<Indices>(manifolds_)).TangentSize() * + Dereference(std::get<Indices>(manifolds_)).AmbientSize())...})}, + ambient_sizes_{ + Dereference(std::get<Indices>(manifolds_)).AmbientSize()...}, + tangent_sizes_{ + Dereference(std::get<Indices>(manifolds_)).TangentSize()...}, + ambient_offsets_{ExclusiveScan(ambient_sizes_)}, + tangent_offsets_{ExclusiveScan(tangent_sizes_)}, + ambient_size_{ + std::accumulate(ambient_sizes_.begin(), ambient_sizes_.end(), 0)}, + tangent_size_{ + std::accumulate(tangent_sizes_.begin(), tangent_sizes_.end(), 0)} {} + + template <std::size_t Index0, std::size_t... Indices> + bool PlusImpl(const double* x, + const double* delta, + double* x_plus_delta, + std::index_sequence<Index0, Indices...>) const { + if (!Dereference(std::get<Index0>(manifolds_)) + .Plus(x + ambient_offsets_[Index0], + delta + tangent_offsets_[Index0], + x_plus_delta + ambient_offsets_[Index0])) { + return false; + } + + return PlusImpl(x, delta, x_plus_delta, std::index_sequence<Indices...>{}); + } + + static constexpr bool PlusImpl(const double* /*x*/, + const double* /*delta*/, + double* /*x_plus_delta*/, + std::index_sequence<>) noexcept { + return true; + } + + template <std::size_t Index0, std::size_t... Indices> + bool MinusImpl(const double* y, + const double* x, + double* y_minus_x, + std::index_sequence<Index0, Indices...>) const { + if (!Dereference(std::get<Index0>(manifolds_)) + .Minus(y + ambient_offsets_[Index0], + x + ambient_offsets_[Index0], + y_minus_x + tangent_offsets_[Index0])) { + return false; + } + + return MinusImpl(y, x, y_minus_x, std::index_sequence<Indices...>{}); + } + + static constexpr bool MinusImpl(const double* /*y*/, + const double* /*x*/, + double* /*y_minus_x*/, + std::index_sequence<>) noexcept { + return true; + } + + template <std::size_t Index0, std::size_t... Indices> + bool PlusJacobianImpl(const double* x, + MatrixRef& jacobian, + internal::FixedArray<double>& buffer, + std::index_sequence<Index0, Indices...>) const { + if (!Dereference(std::get<Index0>(manifolds_)) + .PlusJacobian(x + ambient_offsets_[Index0], buffer.data())) { + return false; + } + + jacobian.block(ambient_offsets_[Index0], + tangent_offsets_[Index0], + ambient_sizes_[Index0], + tangent_sizes_[Index0]) = + MatrixRef( + buffer.data(), ambient_sizes_[Index0], tangent_sizes_[Index0]); + + return PlusJacobianImpl( + x, jacobian, buffer, std::index_sequence<Indices...>{}); + } + + static constexpr bool PlusJacobianImpl( + const double* /*x*/, + MatrixRef& /*jacobian*/, + internal::FixedArray<double>& /*buffer*/, + std::index_sequence<>) noexcept { + return true; + } + + template <std::size_t Index0, std::size_t... Indices> + bool MinusJacobianImpl(const double* x, + MatrixRef& jacobian, + internal::FixedArray<double>& buffer, + std::index_sequence<Index0, Indices...>) const { + if (!Dereference(std::get<Index0>(manifolds_)) + .MinusJacobian(x + ambient_offsets_[Index0], buffer.data())) { + return false; + } + + jacobian.block(tangent_offsets_[Index0], + ambient_offsets_[Index0], + tangent_sizes_[Index0], + ambient_sizes_[Index0]) = + MatrixRef( + buffer.data(), tangent_sizes_[Index0], ambient_sizes_[Index0]); + + return MinusJacobianImpl( + x, jacobian, buffer, std::index_sequence<Indices...>{}); + } + + static constexpr bool MinusJacobianImpl( + const double* /*x*/, + MatrixRef& /*jacobian*/, + internal::FixedArray<double>& /*buffer*/, + std::index_sequence<>) noexcept { + return true; + } + + template <typename T, std::size_t N> + static std::array<T, N> ExclusiveScan(const std::array<T, N>& values) { + std::array<T, N> result; + T init = 0; + + // TODO Replace by std::exclusive_scan once C++17 is available + for (std::size_t i = 0; i != N; ++i) { + result[i] = init; + init += values[i]; + } + + return result; + } + + // TODO Replace by std::void_t once C++17 is available + template <typename... Types> + struct Void { + using type = void; + }; + + template <typename T, typename E = void> + struct IsDereferenceable : std::false_type {}; + + template <typename T> + struct IsDereferenceable<T, typename Void<decltype(*std::declval<T>())>::type> + : std::true_type {}; + + template <typename T, + std::enable_if_t<!IsDereferenceable<T>::value>* = nullptr> + static constexpr decltype(auto) Dereference(T& value) { + return value; + } + + // Support dereferenceable types such as std::unique_ptr, std::shared_ptr, raw + // pointers etc. + template <typename T, + std::enable_if_t<IsDereferenceable<T>::value>* = nullptr> + static constexpr decltype(auto) Dereference(T& value) { + return *value; + } + + template <typename T> + static constexpr decltype(auto) Dereference(T* p) { + assert(p != nullptr); + return *p; + } + + std::tuple<Manifold0, Manifold1, ManifoldN...> manifolds_; + int buffer_size_; + std::array<int, kNumManifolds> ambient_sizes_; + std::array<int, kNumManifolds> tangent_sizes_; + std::array<int, kNumManifolds> ambient_offsets_; + std::array<int, kNumManifolds> tangent_offsets_; + int ambient_size_; + int tangent_size_; +}; + +#ifdef CERES_HAS_CPP17 +// C++17 deduction guide that allows the user to avoid explicitly specifying +// the template parameters of ProductManifold. The class can instead be +// instantiated as follows: +// +// ProductManifold manifold{QuaternionManifold{}, EuclideanManifold<3>{}}; +// +template <typename Manifold0, typename Manifold1, typename... Manifolds> +ProductManifold(Manifold0&&, Manifold1&&, Manifolds&&...) + -> ProductManifold<Manifold0, Manifold1, Manifolds...>; +#endif + +} // namespace ceres + +#endif // CERES_PUBLIC_PRODUCT_MANIFOLD_H_ diff --git a/extern/ceres/include/ceres/rotation.h b/extern/ceres/include/ceres/rotation.h index 0c82a417a2c..51079901aaf 100644 --- a/extern/ceres/include/ceres/rotation.h +++ b/extern/ceres/include/ceres/rotation.h @@ -521,18 +521,18 @@ inline void UnitQuaternionRotatePoint(const T q[4], DCHECK_NE(pt, result) << "Inplace rotation is not supported."; // clang-format off - const T t2 = q[0] * q[1]; - const T t3 = q[0] * q[2]; - const T t4 = q[0] * q[3]; - const T t5 = -q[1] * q[1]; - const T t6 = q[1] * q[2]; - const T t7 = q[1] * q[3]; - const T t8 = -q[2] * q[2]; - const T t9 = q[2] * q[3]; - const T t1 = -q[3] * q[3]; - result[0] = T(2) * ((t8 + t1) * pt[0] + (t6 - t4) * pt[1] + (t3 + t7) * pt[2]) + pt[0]; // NOLINT - result[1] = T(2) * ((t4 + t6) * pt[0] + (t5 + t1) * pt[1] + (t9 - t2) * pt[2]) + pt[1]; // NOLINT - result[2] = T(2) * ((t7 - t3) * pt[0] + (t2 + t9) * pt[1] + (t5 + t8) * pt[2]) + pt[2]; // NOLINT + T uv0 = q[2] * pt[2] - q[3] * pt[1]; + T uv1 = q[3] * pt[0] - q[1] * pt[2]; + T uv2 = q[1] * pt[1] - q[2] * pt[0]; + uv0 += uv0; + uv1 += uv1; + uv2 += uv2; + result[0] = pt[0] + q[0] * uv0; + result[1] = pt[1] + q[0] * uv1; + result[2] = pt[2] + q[0] * uv2; + result[0] += q[2] * uv2 - q[3] * uv1; + result[1] += q[3] * uv0 - q[1] * uv2; + result[2] += q[1] * uv1 - q[2] * uv0; // clang-format on } @@ -624,16 +624,16 @@ inline void AngleAxisRotatePoint(const T angle_axis[3], result[2] = pt[2] * costheta + w_cross_pt[2] * sintheta + w[2] * tmp; } else { // Near zero, the first order Taylor approximation of the rotation - // matrix R corresponding to a vector w and angle w is + // matrix R corresponding to a vector w and angle theta is // // R = I + hat(w) * sin(theta) // // But sintheta ~ theta and theta * w = angle_axis, which gives us // - // R = I + hat(w) + // R = I + hat(angle_axis) // // and actually performing multiplication with the point pt, gives us - // R * pt = pt + w x pt. + // R * pt = pt + angle_axis x pt. // // Switching to the Taylor expansion near zero provides meaningful // derivatives when evaluated using Jets. diff --git a/extern/ceres/include/ceres/sized_cost_function.h b/extern/ceres/include/ceres/sized_cost_function.h index 8e92f1b796c..d76b5c26b4c 100644 --- a/extern/ceres/include/ceres/sized_cost_function.h +++ b/extern/ceres/include/ceres/sized_cost_function.h @@ -61,8 +61,6 @@ class SizedCostFunction : public CostFunction { *mutable_parameter_block_sizes() = std::vector<int32_t>{Ns...}; } - virtual ~SizedCostFunction() {} - // Subclasses must implement Evaluate(). }; diff --git a/extern/ceres/include/ceres/solver.h b/extern/ceres/include/ceres/solver.h index 61b8dd53eb3..026fc1c0830 100644 --- a/extern/ceres/include/ceres/solver.h +++ b/extern/ceres/include/ceres/solver.h @@ -38,8 +38,9 @@ #include <vector> #include "ceres/crs_matrix.h" +#include "ceres/internal/config.h" #include "ceres/internal/disable_warnings.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/iteration_callback.h" #include "ceres/ordered_groups.h" #include "ceres/problem.h" @@ -363,23 +364,23 @@ class CERES_EXPORT Solver { std::unordered_set<ResidualBlockId> residual_blocks_for_subset_preconditioner; - // Ceres supports using multiple dense linear algebra libraries - // for dense matrix factorizations. Currently EIGEN and LAPACK are - // the valid choices. EIGEN is always available, LAPACK refers to - // the system BLAS + LAPACK library which may or may not be + // Ceres supports using multiple dense linear algebra libraries for dense + // matrix factorizations. Currently EIGEN, LAPACK and CUDA are the valid + // choices. EIGEN is always available, LAPACK refers to the system BLAS + + // LAPACK library which may or may not be available. CUDA refers to Nvidia's + // GPU based dense linear algebra library, which may or may not be // available. // - // This setting affects the DENSE_QR, DENSE_NORMAL_CHOLESKY and - // DENSE_SCHUR solvers. For small to moderate sized problem EIGEN - // is a fine choice but for large problems, an optimized LAPACK + - // BLAS implementation can make a substantial difference in - // performance. + // This setting affects the DENSE_QR, DENSE_NORMAL_CHOLESKY and DENSE_SCHUR + // solvers. For small to moderate sized problem EIGEN is a fine choice but + // for large problems, an optimized LAPACK + BLAS or CUDA implementation can + // make a substantial difference in performance. DenseLinearAlgebraLibraryType dense_linear_algebra_library_type = EIGEN; - // Ceres supports using multiple sparse linear algebra libraries - // for sparse matrix ordering and factorizations. Currently, - // SUITE_SPARSE and CX_SPARSE are the valid choices, depending on - // whether they are linked into Ceres at build time. + // Ceres supports using multiple sparse linear algebra libraries for sparse + // matrix ordering and factorizations. Currently, SUITE_SPARSE and CX_SPARSE + // are the valid choices, depending on whether they are linked into Ceres at + // build time. SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type = #if !defined(CERES_NO_SUITESPARSE) SUITE_SPARSE; @@ -423,7 +424,7 @@ class CERES_EXPORT Solver { // each group, Ceres is free to order the parameter blocks as it // chooses. // - // If NULL, then all parameter blocks are assumed to be in the + // If nullptr, then all parameter blocks are assumed to be in the // same group and the solver is free to decide the best // ordering. // @@ -536,8 +537,9 @@ class CERES_EXPORT Solver { // max_num_refinement_iterations to 2-3. // // NOTE2: The following two options are currently only applicable - // if sparse_linear_algebra_library_type is EIGEN_SPARSE and - // linear_solver_type is SPARSE_NORMAL_CHOLESKY, or SPARSE_SCHUR. + // if sparse_linear_algebra_library_type is EIGEN_SPARSE or + // ACCELERATE_SPARSE, and linear_solver_type is SPARSE_NORMAL_CHOLESKY + // or SPARSE_SCHUR. bool use_mixed_precision_solves = false; // Number steps of the iterative refinement process to run when @@ -882,7 +884,7 @@ class CERES_EXPORT Solver { // Dimension of the tangent space of the problem (or the number of // columns in the Jacobian for the problem). This is different // from num_parameters if a parameter block is associated with a - // LocalParameterization + // LocalParameterization/Manifold. int num_effective_parameters = -1; // Number of residual blocks in the problem. @@ -903,7 +905,7 @@ class CERES_EXPORT Solver { // number of columns in the Jacobian for the reduced // problem). This is different from num_parameters_reduced if a // parameter block in the reduced problem is associated with a - // LocalParameterization. + // LocalParameterization/Manifold. int num_effective_parameters_reduced = -1; // Number of residual blocks in the reduced problem. diff --git a/extern/ceres/include/ceres/sphere_manifold.h b/extern/ceres/include/ceres/sphere_manifold.h new file mode 100644 index 00000000000..5d71cbbca9a --- /dev/null +++ b/extern/ceres/include/ceres/sphere_manifold.h @@ -0,0 +1,231 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: vitus@google.com (Mike Vitus) +// jodebo_beck@gmx.de (Johannes Beck) + +#ifndef CERES_PUBLIC_SPHERE_MANIFOLD_H_ +#define CERES_PUBLIC_SPHERE_MANIFOLD_H_ + +#include <Eigen/Core> +#include <algorithm> +#include <array> +#include <memory> +#include <vector> + +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" +#include "ceres/internal/householder_vector.h" +#include "ceres/internal/sphere_manifold_functions.h" +#include "ceres/manifold.h" +#include "ceres/types.h" +#include "glog/logging.h" + +namespace ceres { + +// This provides a manifold on a sphere meaning that the norm of the vector +// stays the same. Such cases often arises in Structure for Motion +// problems. One example where they are used is in representing points whose +// triangulation is ill-conditioned. Here it is advantageous to use an +// over-parameterization since homogeneous vectors can represent points at +// infinity. +// +// The plus operator is defined as +// Plus(x, delta) = +// [sin(0.5 * |delta|) * delta / |delta|, cos(0.5 * |delta|)] * x +// +// The minus operator is defined as +// Minus(x, y) = 2 atan2(nhy, y[-1]) / nhy * hy[0 : size_ - 1] +// with nhy = norm(hy[0 : size_ - 1]) +// +// with * defined as an operator which applies the update orthogonal to x to +// remain on the sphere. The ambient space dimension is required to be greater +// than 1. +// +// The class works with dynamic and static ambient space dimensions. If the +// ambient space dimensions is known at compile time use +// +// SphereManifold<3> manifold; +// +// If the ambient space dimensions is not known at compile time the template +// parameter needs to be set to ceres::DYNAMIC and the actual dimension needs +// to be provided as a constructor argument: +// +// SphereManifold<ceres::DYNAMIC> manifold(ambient_dim); +// +// See section B.2 (p.25) in "Integrating Generic Sensor Fusion Algorithms +// with Sound State Representations through Encapsulation of Manifolds" by C. +// Hertzberg, R. Wagner, U. Frese and L. Schroder for more details +// (https://arxiv.org/pdf/1107.1119.pdf) +template <int AmbientSpaceDimension> +class SphereManifold final : public Manifold { + public: + static_assert( + AmbientSpaceDimension == ceres::DYNAMIC || AmbientSpaceDimension > 1, + "The size of the homogeneous vector needs to be greater than 1."); + static_assert(ceres::DYNAMIC == Eigen::Dynamic, + "ceres::DYNAMIC needs to be the same as Eigen::Dynamic."); + + SphereManifold(); + explicit SphereManifold(int size); + + int AmbientSize() const override { + return AmbientSpaceDimension == ceres::DYNAMIC ? size_ + : AmbientSpaceDimension; + } + int TangentSize() const override { return AmbientSize() - 1; } + + bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const override; + bool PlusJacobian(const double* x, double* jacobian) const override; + + bool Minus(const double* y, + const double* x, + double* y_minus_x) const override; + bool MinusJacobian(const double* x, double* jacobian) const override; + + private: + static constexpr int TangentSpaceDimension = + AmbientSpaceDimension > 0 ? AmbientSpaceDimension - 1 : Eigen::Dynamic; + + using AmbientVector = Eigen::Matrix<double, AmbientSpaceDimension, 1>; + using TangentVector = Eigen::Matrix<double, TangentSpaceDimension, 1>; + using MatrixPlusJacobian = Eigen::Matrix<double, + AmbientSpaceDimension, + TangentSpaceDimension, + Eigen::RowMajor>; + using MatrixMinusJacobian = Eigen::Matrix<double, + TangentSpaceDimension, + AmbientSpaceDimension, + Eigen::RowMajor>; + + const int size_{}; +}; + +template <int AmbientSpaceDimension> +SphereManifold<AmbientSpaceDimension>::SphereManifold() + : size_{AmbientSpaceDimension} { + static_assert( + AmbientSpaceDimension != Eigen::Dynamic, + "The size is set to dynamic. Please call the constructor with a size."); +} + +template <int AmbientSpaceDimension> +SphereManifold<AmbientSpaceDimension>::SphereManifold(int size) : size_{size} { + if (AmbientSpaceDimension != Eigen::Dynamic) { + CHECK_EQ(AmbientSpaceDimension, size) + << "Specified size by template parameter differs from the supplied " + "one."; + } else { + CHECK_GT(size_, 1) + << "The size of the manifold needs to be greater than 1."; + } +} + +template <int AmbientSpaceDimension> +bool SphereManifold<AmbientSpaceDimension>::Plus( + const double* x_ptr, + const double* delta_ptr, + double* x_plus_delta_ptr) const { + Eigen::Map<const AmbientVector> x(x_ptr, size_); + Eigen::Map<const TangentVector> delta(delta_ptr, size_ - 1); + Eigen::Map<AmbientVector> x_plus_delta(x_plus_delta_ptr, size_); + + const double norm_delta = delta.norm(); + + if (norm_delta == 0.0) { + x_plus_delta = x; + return true; + } + + AmbientVector v(size_); + double beta; + + // NOTE: The explicit template arguments are needed here because + // ComputeHouseholderVector is templated and some versions of MSVC + // have trouble deducing the type of v automatically. + internal::ComputeHouseholderVector<Eigen::Map<const AmbientVector>, + double, + AmbientSpaceDimension>(x, &v, &beta); + + internal::ComputeSphereManifoldPlus( + v, beta, x, delta, norm_delta, &x_plus_delta); + + return true; +} + +template <int AmbientSpaceDimension> +bool SphereManifold<AmbientSpaceDimension>::PlusJacobian( + const double* x_ptr, double* jacobian_ptr) const { + Eigen::Map<const AmbientVector> x(x_ptr, size_); + Eigen::Map<MatrixPlusJacobian> jacobian(jacobian_ptr, size_, size_ - 1); + internal::ComputeSphereManifoldPlusJacobian(x, &jacobian); + + return true; +} + +template <int AmbientSpaceDimension> +bool SphereManifold<AmbientSpaceDimension>::Minus(const double* y_ptr, + const double* x_ptr, + double* y_minus_x_ptr) const { + AmbientVector y = Eigen::Map<const AmbientVector>(y_ptr, size_); + Eigen::Map<const AmbientVector> x(x_ptr, size_); + Eigen::Map<TangentVector> y_minus_x(y_minus_x_ptr, size_ - 1); + + // Apply hoseholder transformation. + AmbientVector v(size_); + double beta; + + // NOTE: The explicit template arguments are needed here because + // ComputeHouseholderVector is templated and some versions of MSVC + // have trouble deducing the type of v automatically. + internal::ComputeHouseholderVector<Eigen::Map<const AmbientVector>, + double, + AmbientSpaceDimension>(x, &v, &beta); + internal::ComputeSphereManifoldMinus(v, beta, x, y, &y_minus_x); + return true; +} + +template <int AmbientSpaceDimension> +bool SphereManifold<AmbientSpaceDimension>::MinusJacobian( + const double* x_ptr, double* jacobian_ptr) const { + Eigen::Map<const AmbientVector> x(x_ptr, size_); + Eigen::Map<MatrixMinusJacobian> jacobian(jacobian_ptr, size_ - 1, size_); + + internal::ComputeSphereManifoldMinusJacobian(x, &jacobian); + return true; +} + +} // namespace ceres + +// clang-format off +#include "ceres/internal/reenable_warnings.h" +// clang-format on + +#endif // CERES_PUBLIC_SPHERE_MANIFOLD_H_ diff --git a/extern/ceres/include/ceres/tiny_solver.h b/extern/ceres/include/ceres/tiny_solver.h index 47db5824dc5..86a655dc07d 100644 --- a/extern/ceres/include/ceres/tiny_solver.h +++ b/extern/ceres/include/ceres/tiny_solver.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2019 Google Inc. All rights reserved. +// Copyright 2021 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -84,7 +84,8 @@ namespace ceres { // double* parameters -- NUM_PARAMETERS or NumParameters() // double* residuals -- NUM_RESIDUALS or NumResiduals() // double* jacobian -- NUM_RESIDUALS * NUM_PARAMETERS in column-major format -// (Eigen's default); or NULL if no jacobian requested. +// (Eigen's default); or nullptr if no jacobian +// requested. // // An example (fully statically sized): // @@ -126,8 +127,8 @@ namespace ceres { // template <typename Function, typename LinearSolver = - Eigen::LDLT<Eigen::Matrix<typename Function::Scalar, - Function::NUM_PARAMETERS, + Eigen::LDLT<Eigen::Matrix<typename Function::Scalar, // + Function::NUM_PARAMETERS, // Function::NUM_PARAMETERS>>> class TinySolver { public: @@ -139,41 +140,59 @@ class TinySolver { NUM_RESIDUALS = Function::NUM_RESIDUALS, NUM_PARAMETERS = Function::NUM_PARAMETERS }; - typedef typename Function::Scalar Scalar; - typedef typename Eigen::Matrix<Scalar, NUM_PARAMETERS, 1> Parameters; + using Scalar = typename Function::Scalar; + using Parameters = typename Eigen::Matrix<Scalar, NUM_PARAMETERS, 1>; enum Status { - GRADIENT_TOO_SMALL, // eps > max(J'*f(x)) - RELATIVE_STEP_SIZE_TOO_SMALL, // eps > ||dx|| / (||x|| + eps) - COST_TOO_SMALL, // eps > ||f(x)||^2 / 2 + // max_norm |J'(x) * f(x)| < gradient_tolerance + GRADIENT_TOO_SMALL, + // ||dx|| <= parameter_tolerance * (||x|| + parameter_tolerance) + RELATIVE_STEP_SIZE_TOO_SMALL, + // cost_threshold > ||f(x)||^2 / 2 + COST_TOO_SMALL, + // num_iterations >= max_num_iterations HIT_MAX_ITERATIONS, + // (new_cost - old_cost) < function_tolerance * old_cost + COST_CHANGE_TOO_SMALL, // TODO(sameeragarwal): Deal with numerical failures. }; struct Options { - Scalar gradient_tolerance = 1e-10; // eps > max(J'*f(x)) - Scalar parameter_tolerance = 1e-8; // eps > ||dx|| / ||x|| - Scalar cost_threshold = // eps > ||f(x)|| - std::numeric_limits<Scalar>::epsilon(); - Scalar initial_trust_region_radius = 1e4; int max_num_iterations = 50; + + // max_norm |J'(x) * f(x)| < gradient_tolerance + Scalar gradient_tolerance = 1e-10; + + // ||dx|| <= parameter_tolerance * (||x|| + parameter_tolerance) + Scalar parameter_tolerance = 1e-8; + + // (new_cost - old_cost) < function_tolerance * old_cost + Scalar function_tolerance = 1e-6; + + // cost_threshold > ||f(x)||^2 / 2 + Scalar cost_threshold = std::numeric_limits<Scalar>::epsilon(); + + Scalar initial_trust_region_radius = 1e4; }; struct Summary { - Scalar initial_cost = -1; // 1/2 ||f(x)||^2 - Scalar final_cost = -1; // 1/2 ||f(x)||^2 - Scalar gradient_max_norm = -1; // max(J'f(x)) + // 1/2 ||f(x_0)||^2 + Scalar initial_cost = -1; + // 1/2 ||f(x)||^2 + Scalar final_cost = -1; + // max_norm(J'f(x)) + Scalar gradient_max_norm = -1; int iterations = -1; Status status = HIT_MAX_ITERATIONS; }; bool Update(const Function& function, const Parameters& x) { - if (!function(x.data(), error_.data(), jacobian_.data())) { + if (!function(x.data(), residuals_.data(), jacobian_.data())) { return false; } - error_ = -error_; + residuals_ = -residuals_; // On the first iteration, compute a diagonal (Jacobi) scaling // matrix, which we store as a vector. @@ -192,9 +211,9 @@ class TinySolver { // factorization. jacobian_ = jacobian_ * jacobi_scaling_.asDiagonal(); jtj_ = jacobian_.transpose() * jacobian_; - g_ = jacobian_.transpose() * error_; + g_ = jacobian_.transpose() * residuals_; summary.gradient_max_norm = g_.array().abs().maxCoeff(); - cost_ = error_.squaredNorm() / 2; + cost_ = residuals_.squaredNorm() / 2; return true; } @@ -231,7 +250,7 @@ class TinySolver { const Scalar max_diagonal = 1e32; for (int i = 0; i < lm_diagonal_.rows(); ++i) { lm_diagonal_[i] = std::sqrt( - u * std::min(std::max(jtj_(i, i), min_diagonal), max_diagonal)); + u * (std::min)((std::max)(jtj_(i, i), min_diagonal), max_diagonal)); jtj_regularized_(i, i) += lm_diagonal_[i] * lm_diagonal_[i]; } @@ -253,10 +272,9 @@ class TinySolver { // TODO(keir): Add proper handling of errors from user eval of cost // functions. - function(&x_new_[0], &f_x_new_[0], NULL); + function(&x_new_[0], &f_x_new_[0], nullptr); const Scalar cost_change = (2 * cost_ - f_x_new_.squaredNorm()); - // TODO(sameeragarwal): Better more numerically stable evaluation. const Scalar model_cost_change = lm_step_.dot(2 * g_ - jtj_ * lm_step_); @@ -269,6 +287,12 @@ class TinySolver { // model fits well. x = x_new_; + if (std::abs(cost_change) < options.function_tolerance) { + cost_ = f_x_new_.squaredNorm() / 2; + summary.status = COST_CHANGE_TOO_SMALL; + break; + } + // TODO(sameeragarwal): Deal with failure. Update(function, x); if (summary.gradient_max_norm < options.gradient_tolerance) { @@ -282,16 +306,24 @@ class TinySolver { } Scalar tmp = Scalar(2 * rho - 1); - u = u * std::max(1 / 3., 1 - tmp * tmp * tmp); + u = u * (std::max)(Scalar(1 / 3.), Scalar(1) - tmp * tmp * tmp); v = 2; - continue; - } - // Reject the update because either the normal equations failed to solve - // or the local linear model was not good (rho < 0). Instead, increase u - // to move closer to gradient descent. - u *= v; - v *= 2; + } else { + // Reject the update because either the normal equations failed to solve + // or the local linear model was not good (rho < 0). + + // Additionally if the cost change is too small, then terminate. + if (std::abs(cost_change) < options.function_tolerance) { + // Terminate + summary.status = COST_CHANGE_TOO_SMALL; + break; + } + + // Reduce the size of the trust region. + u *= v; + v *= 2; + } } summary.final_cost = cost_; @@ -307,7 +339,7 @@ class TinySolver { LinearSolver linear_solver_; Scalar cost_; Parameters dx_, x_new_, g_, jacobi_scaling_, lm_diagonal_, lm_step_; - Eigen::Matrix<Scalar, NUM_RESIDUALS, 1> error_, f_x_new_; + Eigen::Matrix<Scalar, NUM_RESIDUALS, 1> residuals_, f_x_new_; Eigen::Matrix<Scalar, NUM_RESIDUALS, NUM_PARAMETERS> jacobian_; Eigen::Matrix<Scalar, NUM_PARAMETERS, NUM_PARAMETERS> jtj_, jtj_regularized_; @@ -317,7 +349,7 @@ class TinySolver { template <typename T> struct enable_if<true, T> { - typedef T type; + using type = T; }; // The number of parameters and residuals are dynamically sized. @@ -355,7 +387,7 @@ class TinySolver { jacobi_scaling_.resize(num_parameters); lm_diagonal_.resize(num_parameters); lm_step_.resize(num_parameters); - error_.resize(num_residuals); + residuals_.resize(num_residuals); f_x_new_.resize(num_residuals); jacobian_.resize(num_residuals, num_parameters); jtj_.resize(num_parameters, num_parameters); diff --git a/extern/ceres/include/ceres/tiny_solver_autodiff_function.h b/extern/ceres/include/ceres/tiny_solver_autodiff_function.h index b782f549cc1..3e3675ff070 100644 --- a/extern/ceres/include/ceres/tiny_solver_autodiff_function.h +++ b/extern/ceres/include/ceres/tiny_solver_autodiff_function.h @@ -113,12 +113,12 @@ class TinySolverAutoDiffFunction { // as a member a Jet type, which itself has a fixed-size Eigen type as member. EIGEN_MAKE_ALIGNED_OPERATOR_NEW - TinySolverAutoDiffFunction(const CostFunctor& cost_functor) + explicit TinySolverAutoDiffFunction(const CostFunctor& cost_functor) : cost_functor_(cost_functor) { Initialize<kNumResiduals>(cost_functor); } - typedef T Scalar; + using Scalar = T; enum { NUM_PARAMETERS = kNumParameters, NUM_RESIDUALS = kNumResiduals, @@ -127,7 +127,7 @@ class TinySolverAutoDiffFunction { // This is similar to AutoDifferentiate(), but since there is only one // parameter block it is easier to inline to avoid overhead. bool operator()(const T* parameters, T* residuals, T* jacobian) const { - if (jacobian == NULL) { + if (jacobian == nullptr) { // No jacobian requested, so just directly call the cost function with // doubles, skipping jets and derivatives. return cost_functor_(parameters, residuals); diff --git a/extern/ceres/include/ceres/tiny_solver_cost_function_adapter.h b/extern/ceres/include/ceres/tiny_solver_cost_function_adapter.h index 18ccb398f90..cc5ca16af5d 100644 --- a/extern/ceres/include/ceres/tiny_solver_cost_function_adapter.h +++ b/extern/ceres/include/ceres/tiny_solver_cost_function_adapter.h @@ -75,7 +75,7 @@ template <int kNumResiduals = Eigen::Dynamic, int kNumParameters = Eigen::Dynamic> class TinySolverCostFunctionAdapter { public: - typedef double Scalar; + using Scalar = double; enum ComponentSizeType { NUM_PARAMETERS = kNumParameters, NUM_RESIDUALS = kNumResiduals @@ -85,7 +85,7 @@ class TinySolverCostFunctionAdapter { // fixed-size Eigen types. EIGEN_MAKE_ALIGNED_OPERATOR_NEW - TinySolverCostFunctionAdapter(const CostFunction& cost_function) + explicit TinySolverCostFunctionAdapter(const CostFunction& cost_function) : cost_function_(cost_function) { CHECK_EQ(cost_function_.parameter_block_sizes().size(), 1) << "Only CostFunctions with exactly one parameter blocks are allowed."; @@ -108,7 +108,7 @@ class TinySolverCostFunctionAdapter { double* residuals, double* jacobian) const { if (!jacobian) { - return cost_function_.Evaluate(¶meters, residuals, NULL); + return cost_function_.Evaluate(¶meters, residuals, nullptr); } double* jacobians[1] = {row_major_jacobian_.data()}; diff --git a/extern/ceres/include/ceres/types.h b/extern/ceres/include/ceres/types.h index 5ee6fdca576..e5224238129 100644 --- a/extern/ceres/include/ceres/types.h +++ b/extern/ceres/include/ceres/types.h @@ -40,7 +40,7 @@ #include <string> #include "ceres/internal/disable_warnings.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { @@ -186,6 +186,7 @@ enum SparseLinearAlgebraLibraryType { enum DenseLinearAlgebraLibraryType { EIGEN, LAPACK, + CUDA, }; // Logging options diff --git a/extern/ceres/include/ceres/version.h b/extern/ceres/include/ceres/version.h index a76cc1099c5..e0d61972896 100644 --- a/extern/ceres/include/ceres/version.h +++ b/extern/ceres/include/ceres/version.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2019 Google Inc. All rights reserved. +// Copyright 2021 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ #define CERES_PUBLIC_VERSION_H_ #define CERES_VERSION_MAJOR 2 -#define CERES_VERSION_MINOR 0 +#define CERES_VERSION_MINOR 1 #define CERES_VERSION_REVISION 0 // Classic CPP stringifcation; the extra level of indirection allows the diff --git a/extern/ceres/internal/ceres/accelerate_sparse.cc b/extern/ceres/internal/ceres/accelerate_sparse.cc index d2b642bf5dc..74adfaf9afc 100644 --- a/extern/ceres/internal/ceres/accelerate_sparse.cc +++ b/extern/ceres/internal/ceres/accelerate_sparse.cc @@ -29,11 +29,12 @@ // Author: alexs.mac@gmail.com (Alex Stewart) // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_NO_ACCELERATE_SPARSE #include <algorithm> +#include <memory> #include <string> #include <vector> @@ -196,17 +197,17 @@ template <typename Scalar> LinearSolverTerminationType AppleAccelerateCholesky<Scalar>::Factorize( CompressedRowSparseMatrix* lhs, std::string* message) { CHECK_EQ(lhs->storage_type(), StorageType()); - if (lhs == NULL) { - *message = "Failure: Input lhs is NULL."; + if (lhs == nullptr) { + *message = "Failure: Input lhs is nullptr."; return LINEAR_SOLVER_FATAL_ERROR; } typename SparseTypesTrait<Scalar>::SparseMatrix as_lhs = as_.CreateSparseMatrixTransposeView(lhs); if (!symbolic_factor_) { - symbolic_factor_.reset( - new typename SparseTypesTrait<Scalar>::SymbolicFactorization( - as_.AnalyzeCholesky(&as_lhs))); + symbolic_factor_ = std::make_unique< + typename SparseTypesTrait<Scalar>::SymbolicFactorization>( + as_.AnalyzeCholesky(&as_lhs)); if (symbolic_factor_->status != SparseStatusOK) { *message = StringPrintf( "Apple Accelerate Failure : Symbolic factorisation failed: %s", @@ -217,9 +218,9 @@ LinearSolverTerminationType AppleAccelerateCholesky<Scalar>::Factorize( } if (!numeric_factor_) { - numeric_factor_.reset( - new typename SparseTypesTrait<Scalar>::NumericFactorization( - as_.Cholesky(&as_lhs, symbolic_factor_.get()))); + numeric_factor_ = std::make_unique< + typename SparseTypesTrait<Scalar>::NumericFactorization>( + as_.Cholesky(&as_lhs, symbolic_factor_.get())); } else { // Recycle memory from previous numeric factorization. as_.Cholesky(&as_lhs, numeric_factor_.get()); @@ -265,7 +266,7 @@ template <typename Scalar> void AppleAccelerateCholesky<Scalar>::FreeSymbolicFactorization() { if (symbolic_factor_) { SparseCleanup(*symbolic_factor_); - symbolic_factor_.reset(); + symbolic_factor_ = nullptr; } } @@ -273,7 +274,7 @@ template <typename Scalar> void AppleAccelerateCholesky<Scalar>::FreeNumericFactorization() { if (numeric_factor_) { SparseCleanup(*numeric_factor_); - numeric_factor_.reset(); + numeric_factor_ = nullptr; } } diff --git a/extern/ceres/internal/ceres/accelerate_sparse.h b/extern/ceres/internal/ceres/accelerate_sparse.h index e53758dfa15..29d78e8c261 100644 --- a/extern/ceres/internal/ceres/accelerate_sparse.h +++ b/extern/ceres/internal/ceres/accelerate_sparse.h @@ -32,7 +32,7 @@ #define CERES_INTERNAL_ACCELERATE_SPARSE_H_ // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_NO_ACCELERATE_SPARSE @@ -111,7 +111,7 @@ class AccelerateSparse { // An implementation of SparseCholesky interface using Apple's Accelerate // framework. template <typename Scalar> -class AppleAccelerateCholesky : public SparseCholesky { +class AppleAccelerateCholesky final : public SparseCholesky { public: // Factory static std::unique_ptr<SparseCholesky> Create(OrderingType ordering_type); diff --git a/extern/ceres/internal/ceres/array_utils.cc b/extern/ceres/internal/ceres/array_utils.cc index 6bffd840f4b..113d41c927e 100644 --- a/extern/ceres/internal/ceres/array_utils.cc +++ b/extern/ceres/internal/ceres/array_utils.cc @@ -44,7 +44,7 @@ namespace internal { using std::string; bool IsArrayValid(const int size, const double* x) { - if (x != NULL) { + if (x != nullptr) { for (int i = 0; i < size; ++i) { if (!std::isfinite(x[i]) || (x[i] == kImpossibleValue)) { return false; @@ -55,7 +55,7 @@ bool IsArrayValid(const int size, const double* x) { } int FindInvalidValue(const int size, const double* x) { - if (x == NULL) { + if (x == nullptr) { return size; } @@ -69,7 +69,7 @@ int FindInvalidValue(const int size, const double* x) { } void InvalidateArray(const int size, double* x) { - if (x != NULL) { + if (x != nullptr) { for (int i = 0; i < size; ++i) { x[i] = kImpossibleValue; } @@ -78,7 +78,7 @@ void InvalidateArray(const int size, double* x) { void AppendArrayToString(const int size, const double* x, string* result) { for (int i = 0; i < size; ++i) { - if (x == NULL) { + if (x == nullptr) { StringAppendF(result, "Not Computed "); } else { if (x[i] == kImpossibleValue) { diff --git a/extern/ceres/internal/ceres/array_utils.h b/extern/ceres/internal/ceres/array_utils.h index 68feca5e792..d2fc7914e1b 100644 --- a/extern/ceres/internal/ceres/array_utils.h +++ b/extern/ceres/internal/ceres/array_utils.h @@ -45,29 +45,30 @@ #include <string> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { // Fill the array x with an impossible value that the user code is // never expected to compute. -CERES_EXPORT_INTERNAL void InvalidateArray(int size, double* x); +CERES_NO_EXPORT void InvalidateArray(int size, double* x); // Check if all the entries of the array x are valid, i.e. all the // values in the array should be finite and none of them should be // equal to the "impossible" value used by InvalidateArray. -CERES_EXPORT_INTERNAL bool IsArrayValid(int size, const double* x); +CERES_NO_EXPORT bool IsArrayValid(int size, const double* x); // If the array contains an invalid value, return the index for it, // otherwise return size. -CERES_EXPORT_INTERNAL int FindInvalidValue(const int size, const double* x); +CERES_NO_EXPORT int FindInvalidValue(const int size, const double* x); // Utility routine to print an array of doubles to a string. If the -// array pointer is NULL, it is treated as an array of zeros. -CERES_EXPORT_INTERNAL void AppendArrayToString(const int size, - const double* x, - std::string* result); +// array pointer is nullptr, it is treated as an array of zeros. +CERES_NO_EXPORT void AppendArrayToString(const int size, + const double* x, + std::string* result); // This routine takes an array of integer values, sorts and uniques // them and then maps each value in the array to its position in the @@ -82,9 +83,11 @@ CERES_EXPORT_INTERNAL void AppendArrayToString(const int size, // gets mapped to // // [1 0 2 3 0 1 3] -CERES_EXPORT_INTERNAL void MapValuesToContiguousRange(int size, int* array); +CERES_NO_EXPORT void MapValuesToContiguousRange(int size, int* array); } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_ARRAY_UTILS_H_ diff --git a/extern/ceres/internal/ceres/block_evaluate_preparer.cc b/extern/ceres/internal/ceres/block_evaluate_preparer.cc index 7db96d94e0a..56c97b60cc4 100644 --- a/extern/ceres/internal/ceres/block_evaluate_preparer.cc +++ b/extern/ceres/internal/ceres/block_evaluate_preparer.cc @@ -53,7 +53,7 @@ void BlockEvaluatePreparer::Prepare(const ResidualBlock* residual_block, SparseMatrix* jacobian, double** jacobians) { // If the overall jacobian is not available, use the scratch space. - if (jacobian == NULL) { + if (jacobian == nullptr) { scratch_evaluate_preparer_.Prepare( residual_block, residual_block_index, jacobian, jacobians); return; @@ -73,7 +73,7 @@ void BlockEvaluatePreparer::Prepare(const ResidualBlock* residual_block, // parameters. Instead, bump the pointer for active parameters only. jacobian_block_offset++; } else { - jacobians[j] = NULL; + jacobians[j] = nullptr; } } } diff --git a/extern/ceres/internal/ceres/block_evaluate_preparer.h b/extern/ceres/internal/ceres/block_evaluate_preparer.h index 4378689729f..d72e41ba3e4 100644 --- a/extern/ceres/internal/ceres/block_evaluate_preparer.h +++ b/extern/ceres/internal/ceres/block_evaluate_preparer.h @@ -36,6 +36,7 @@ #ifndef CERES_INTERNAL_BLOCK_EVALUATE_PREPARER_H_ #define CERES_INTERNAL_BLOCK_EVALUATE_PREPARER_H_ +#include "ceres/internal/export.h" #include "ceres/scratch_evaluate_preparer.h" namespace ceres { @@ -44,7 +45,7 @@ namespace internal { class ResidualBlock; class SparseMatrix; -class BlockEvaluatePreparer { +class CERES_NO_EXPORT BlockEvaluatePreparer { public: // Using Init() instead of a constructor allows for allocating this structure // with new[]. This is because C++ doesn't allow passing arguments to objects diff --git a/extern/ceres/internal/ceres/block_jacobi_preconditioner.cc b/extern/ceres/internal/ceres/block_jacobi_preconditioner.cc index 6f37aca553c..6e979dea93b 100644 --- a/extern/ceres/internal/ceres/block_jacobi_preconditioner.cc +++ b/extern/ceres/internal/ceres/block_jacobi_preconditioner.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -47,10 +47,10 @@ BlockJacobiPreconditioner::BlockJacobiPreconditioner( blocks[i] = bs->cols[i].size; } - m_.reset(new BlockRandomAccessDiagonalMatrix(blocks)); + m_ = std::make_unique<BlockRandomAccessDiagonalMatrix>(blocks); } -BlockJacobiPreconditioner::~BlockJacobiPreconditioner() {} +BlockJacobiPreconditioner::~BlockJacobiPreconditioner() = default; bool BlockJacobiPreconditioner::UpdateImpl(const BlockSparseMatrix& A, const double* D) { @@ -60,21 +60,20 @@ bool BlockJacobiPreconditioner::UpdateImpl(const BlockSparseMatrix& A, for (int i = 0; i < bs->rows.size(); ++i) { const int row_block_size = bs->rows[i].block.size; const std::vector<Cell>& cells = bs->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - const int block_id = cells[j].block_id; + for (const auto& cell : cells) { + const int block_id = cell.block_id; const int col_block_size = bs->cols[block_id].size; int r, c, row_stride, col_stride; CellInfo* cell_info = m_->GetCell(block_id, block_id, &r, &c, &row_stride, &col_stride); MatrixRef m(cell_info->values, row_stride, col_stride); - ConstMatrixRef b( - values + cells[j].position, row_block_size, col_block_size); + ConstMatrixRef b(values + cell.position, row_block_size, col_block_size); m.block(r, c, col_block_size, col_block_size) += b.transpose() * b; } } - if (D != NULL) { + if (D != nullptr) { // Add the diagonal. int position = 0; for (int i = 0; i < bs->cols.size(); ++i) { diff --git a/extern/ceres/internal/ceres/block_jacobi_preconditioner.h b/extern/ceres/internal/ceres/block_jacobi_preconditioner.h index 18f749533e0..e0a512a1469 100644 --- a/extern/ceres/internal/ceres/block_jacobi_preconditioner.h +++ b/extern/ceres/internal/ceres/block_jacobi_preconditioner.h @@ -34,7 +34,8 @@ #include <memory> #include "ceres/block_random_access_diagonal_matrix.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/preconditioner.h" namespace ceres { @@ -53,7 +54,7 @@ struct CompressedRowBlockStructure; // update the matrix by running Update(A, D). The values of the matrix A are // inspected to construct the preconditioner. The vector D is applied as the // D^TD diagonal term. -class CERES_EXPORT_INTERNAL BlockJacobiPreconditioner +class CERES_NO_EXPORT BlockJacobiPreconditioner : public BlockSparseMatrixPreconditioner { public: // A must remain valid while the BlockJacobiPreconditioner is. @@ -61,7 +62,7 @@ class CERES_EXPORT_INTERNAL BlockJacobiPreconditioner BlockJacobiPreconditioner(const BlockJacobiPreconditioner&) = delete; void operator=(const BlockJacobiPreconditioner&) = delete; - virtual ~BlockJacobiPreconditioner(); + ~BlockJacobiPreconditioner() override; // Preconditioner interface void RightMultiply(const double* x, double* y) const final; @@ -78,4 +79,6 @@ class CERES_EXPORT_INTERNAL BlockJacobiPreconditioner } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_BLOCK_JACOBI_PRECONDITIONER_H_ diff --git a/extern/ceres/internal/ceres/block_jacobian_writer.cc b/extern/ceres/internal/ceres/block_jacobian_writer.cc index 17c157b47f9..a70660f860a 100644 --- a/extern/ceres/internal/ceres/block_jacobian_writer.cc +++ b/extern/ceres/internal/ceres/block_jacobian_writer.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -30,10 +30,13 @@ #include "ceres/block_jacobian_writer.h" +#include <algorithm> +#include <memory> + #include "ceres/block_evaluate_preparer.h" #include "ceres/block_sparse_matrix.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/parameter_block.h" #include "ceres/program.h" #include "ceres/residual_block.h" @@ -66,8 +69,7 @@ void BuildJacobianLayout(const Program& program, // matrix. Also compute the number of jacobian blocks. int f_block_pos = 0; int num_jacobian_blocks = 0; - for (int i = 0; i < residual_blocks.size(); ++i) { - ResidualBlock* residual_block = residual_blocks[i]; + for (auto* residual_block : residual_blocks) { const int num_residuals = residual_block->NumResiduals(); const int num_parameter_blocks = residual_block->NumParameterBlocks(); @@ -78,7 +80,7 @@ void BuildJacobianLayout(const Program& program, // Only count blocks for active parameters. num_jacobian_blocks++; if (parameter_block->index() < num_eliminate_blocks) { - f_block_pos += num_residuals * parameter_block->LocalSize(); + f_block_pos += num_residuals * parameter_block->TangentSize(); } } } @@ -107,7 +109,7 @@ void BuildJacobianLayout(const Program& program, continue; } const int jacobian_block_size = - num_residuals * parameter_block->LocalSize(); + num_residuals * parameter_block->TangentSize(); if (parameter_block_index < num_eliminate_blocks) { *jacobian_pos = e_block_pos; e_block_pos += jacobian_block_size; @@ -136,20 +138,20 @@ BlockJacobianWriter::BlockJacobianWriter(const Evaluator::Options& options, // Create evaluate prepareres that point directly into the final jacobian. This // makes the final Write() a nop. -BlockEvaluatePreparer* BlockJacobianWriter::CreateEvaluatePreparers( - int num_threads) { +std::unique_ptr<BlockEvaluatePreparer[]> +BlockJacobianWriter::CreateEvaluatePreparers(int num_threads) { int max_derivatives_per_residual_block = program_->MaxDerivativesPerResidualBlock(); - BlockEvaluatePreparer* preparers = new BlockEvaluatePreparer[num_threads]; + auto preparers = std::make_unique<BlockEvaluatePreparer[]>(num_threads); for (int i = 0; i < num_threads; i++) { preparers[i].Init(&jacobian_layout_[0], max_derivatives_per_residual_block); } return preparers; } -SparseMatrix* BlockJacobianWriter::CreateJacobian() const { - CompressedRowBlockStructure* bs = new CompressedRowBlockStructure; +std::unique_ptr<SparseMatrix> BlockJacobianWriter::CreateJacobian() const { + auto* bs = new CompressedRowBlockStructure; const vector<ParameterBlock*>& parameter_blocks = program_->parameter_blocks(); @@ -159,7 +161,7 @@ SparseMatrix* BlockJacobianWriter::CreateJacobian() const { for (int i = 0, cursor = 0; i < parameter_blocks.size(); ++i) { CHECK_NE(parameter_blocks[i]->index(), -1); CHECK(!parameter_blocks[i]->IsConstant()); - bs->cols[i].size = parameter_blocks[i]->LocalSize(); + bs->cols[i].size = parameter_blocks[i]->TangentSize(); bs->cols[i].position = cursor; cursor += bs->cols[i].size; } @@ -201,12 +203,10 @@ SparseMatrix* BlockJacobianWriter::CreateJacobian() const { } } - sort(row->cells.begin(), row->cells.end(), CellLessThan); + std::sort(row->cells.begin(), row->cells.end(), CellLessThan); } - BlockSparseMatrix* jacobian = new BlockSparseMatrix(bs); - CHECK(jacobian != nullptr); - return jacobian; + return std::make_unique<BlockSparseMatrix>(bs); } } // namespace internal diff --git a/extern/ceres/internal/ceres/block_jacobian_writer.h b/extern/ceres/internal/ceres/block_jacobian_writer.h index 8054d7b33aa..b2d0aaa3b73 100644 --- a/extern/ceres/internal/ceres/block_jacobian_writer.h +++ b/extern/ceres/internal/ceres/block_jacobian_writer.h @@ -38,10 +38,11 @@ #ifndef CERES_INTERNAL_BLOCK_JACOBIAN_WRITER_H_ #define CERES_INTERNAL_BLOCK_JACOBIAN_WRITER_H_ +#include <memory> #include <vector> #include "ceres/evaluator.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -51,7 +52,7 @@ class Program; class SparseMatrix; // TODO(sameeragarwal): This class needs documemtation. -class BlockJacobianWriter { +class CERES_NO_EXPORT BlockJacobianWriter { public: BlockJacobianWriter(const Evaluator::Options& options, Program* program); @@ -59,9 +60,10 @@ class BlockJacobianWriter { // Create evaluate prepareres that point directly into the final jacobian. // This makes the final Write() a nop. - BlockEvaluatePreparer* CreateEvaluatePreparers(int num_threads); + std::unique_ptr<BlockEvaluatePreparer[]> CreateEvaluatePreparers( + int num_threads); - SparseMatrix* CreateJacobian() const; + std::unique_ptr<SparseMatrix> CreateJacobian() const; void Write(int /* residual_id */, int /* residual_offset */, diff --git a/extern/ceres/internal/ceres/block_random_access_dense_matrix.cc b/extern/ceres/internal/ceres/block_random_access_dense_matrix.cc index 386f81eae77..ed172de1d82 100644 --- a/extern/ceres/internal/ceres/block_random_access_dense_matrix.cc +++ b/extern/ceres/internal/ceres/block_random_access_dense_matrix.cc @@ -48,9 +48,9 @@ BlockRandomAccessDenseMatrix::BlockRandomAccessDenseMatrix( num_rows_ += blocks[i]; } - values_.reset(new double[num_rows_ * num_rows_]); + values_ = std::make_unique<double[]>(num_rows_ * num_rows_); - cell_infos_.reset(new CellInfo[num_blocks * num_blocks]); + cell_infos_ = std::make_unique<CellInfo[]>(num_blocks * num_blocks); for (int i = 0; i < num_blocks * num_blocks; ++i) { cell_infos_[i].values = values_.get(); } @@ -60,7 +60,7 @@ BlockRandomAccessDenseMatrix::BlockRandomAccessDenseMatrix( // Assume that the user does not hold any locks on any cell blocks // when they are calling SetZero. -BlockRandomAccessDenseMatrix::~BlockRandomAccessDenseMatrix() {} +BlockRandomAccessDenseMatrix::~BlockRandomAccessDenseMatrix() = default; CellInfo* BlockRandomAccessDenseMatrix::GetCell(const int row_block_id, const int col_block_id, diff --git a/extern/ceres/internal/ceres/block_random_access_dense_matrix.h b/extern/ceres/internal/ceres/block_random_access_dense_matrix.h index 9e555242994..171a6d694b5 100644 --- a/extern/ceres/internal/ceres/block_random_access_dense_matrix.h +++ b/extern/ceres/internal/ceres/block_random_access_dense_matrix.h @@ -35,7 +35,8 @@ #include <vector> #include "ceres/block_random_access_matrix.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -46,11 +47,11 @@ namespace internal { // num_rows x num_cols. // // This class is NOT thread safe. Since all n^2 cells are stored, -// GetCell never returns NULL for any (row_block_id, col_block_id) +// GetCell never returns nullptr for any (row_block_id, col_block_id) // pair. // // ReturnCell is a nop. -class CERES_EXPORT_INTERNAL BlockRandomAccessDenseMatrix +class CERES_NO_EXPORT BlockRandomAccessDenseMatrix : public BlockRandomAccessMatrix { public: // blocks is a vector of block sizes. The resulting matrix has @@ -61,7 +62,7 @@ class CERES_EXPORT_INTERNAL BlockRandomAccessDenseMatrix // The destructor is not thread safe. It assumes that no one is // modifying any cells when the matrix is being destroyed. - virtual ~BlockRandomAccessDenseMatrix(); + ~BlockRandomAccessDenseMatrix() override; // BlockRandomAccessMatrix interface. CellInfo* GetCell(int row_block_id, @@ -94,4 +95,6 @@ class CERES_EXPORT_INTERNAL BlockRandomAccessDenseMatrix } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_BLOCK_RANDOM_ACCESS_DENSE_MATRIX_H_ diff --git a/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.cc b/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.cc index 08f6d7f1750..f55f3b30c61 100644 --- a/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.cc +++ b/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -31,12 +31,13 @@ #include "ceres/block_random_access_diagonal_matrix.h" #include <algorithm> +#include <memory> #include <set> #include <utility> #include <vector> #include "Eigen/Dense" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/stl_util.h" #include "ceres/triplet_sparse_matrix.h" #include "ceres/types.h" @@ -57,16 +58,17 @@ BlockRandomAccessDiagonalMatrix::BlockRandomAccessDiagonalMatrix( int num_cols = 0; int num_nonzeros = 0; vector<int> block_positions; - for (int i = 0; i < blocks_.size(); ++i) { + for (int block_size : blocks_) { block_positions.push_back(num_cols); - num_cols += blocks_[i]; - num_nonzeros += blocks_[i] * blocks_[i]; + num_cols += block_size; + num_nonzeros += block_size * block_size; } VLOG(1) << "Matrix Size [" << num_cols << "," << num_cols << "] " << num_nonzeros; - tsm_.reset(new TripletSparseMatrix(num_cols, num_cols, num_nonzeros)); + tsm_ = + std::make_unique<TripletSparseMatrix>(num_cols, num_cols, num_nonzeros); tsm_->set_num_nonzeros(num_nonzeros); int* rows = tsm_->mutable_rows(); int* cols = tsm_->mutable_cols(); @@ -99,7 +101,7 @@ CellInfo* BlockRandomAccessDiagonalMatrix::GetCell(int row_block_id, int* row_stride, int* col_stride) { if (row_block_id != col_block_id) { - return NULL; + return nullptr; } const int stride = blocks_[row_block_id]; @@ -121,8 +123,7 @@ void BlockRandomAccessDiagonalMatrix::SetZero() { void BlockRandomAccessDiagonalMatrix::Invert() { double* values = tsm_->mutable_values(); - for (int i = 0; i < blocks_.size(); ++i) { - const int block_size = blocks_[i]; + for (int block_size : blocks_) { MatrixRef block(values, block_size, block_size); block = block.selfadjointView<Eigen::Upper>().llt().solve( Matrix::Identity(block_size, block_size)); @@ -135,8 +136,7 @@ void BlockRandomAccessDiagonalMatrix::RightMultiply(const double* x, CHECK(x != nullptr); CHECK(y != nullptr); const double* values = tsm_->values(); - for (int i = 0; i < blocks_.size(); ++i) { - const int block_size = blocks_[i]; + for (int block_size : blocks_) { ConstMatrixRef block(values, block_size, block_size); VectorRef(y, block_size).noalias() += block * ConstVectorRef(x, block_size); x += block_size; diff --git a/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.h b/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.h index 3fe7c1e5b22..3d36c378320 100644 --- a/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.h +++ b/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.h @@ -37,7 +37,8 @@ #include <vector> #include "ceres/block_random_access_matrix.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/triplet_sparse_matrix.h" #include "ceres/types.h" @@ -46,7 +47,7 @@ namespace internal { // A thread safe block diagonal matrix implementation of // BlockRandomAccessMatrix. -class CERES_EXPORT_INTERNAL BlockRandomAccessDiagonalMatrix +class CERES_NO_EXPORT BlockRandomAccessDiagonalMatrix : public BlockRandomAccessMatrix { public: // blocks is an array of block sizes. @@ -57,7 +58,7 @@ class CERES_EXPORT_INTERNAL BlockRandomAccessDiagonalMatrix // The destructor is not thread safe. It assumes that no one is // modifying any cells when the matrix is being destroyed. - virtual ~BlockRandomAccessDiagonalMatrix(); + ~BlockRandomAccessDiagonalMatrix() override; // BlockRandomAccessMatrix Interface. CellInfo* GetCell(int row_block_id, @@ -98,4 +99,6 @@ class CERES_EXPORT_INTERNAL BlockRandomAccessDiagonalMatrix } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_BLOCK_RANDOM_ACCESS_DIAGONAL_MATRIX_H_ diff --git a/extern/ceres/internal/ceres/block_random_access_matrix.cc b/extern/ceres/internal/ceres/block_random_access_matrix.cc index ea88855b59a..8e70c049796 100644 --- a/extern/ceres/internal/ceres/block_random_access_matrix.cc +++ b/extern/ceres/internal/ceres/block_random_access_matrix.cc @@ -33,7 +33,7 @@ namespace ceres { namespace internal { -BlockRandomAccessMatrix::~BlockRandomAccessMatrix() {} +BlockRandomAccessMatrix::~BlockRandomAccessMatrix() = default; } // namespace internal } // namespace ceres diff --git a/extern/ceres/internal/ceres/block_random_access_matrix.h b/extern/ceres/internal/ceres/block_random_access_matrix.h index f190622eafe..48759b79a18 100644 --- a/extern/ceres/internal/ceres/block_random_access_matrix.h +++ b/extern/ceres/internal/ceres/block_random_access_matrix.h @@ -35,7 +35,7 @@ #include <mutex> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -62,7 +62,7 @@ namespace internal { // // There is no requirement that all cells be present, i.e. the matrix // itself can be block sparse. When a cell is not present, the GetCell -// method will return a NULL pointer. +// method will return a nullptr pointer. // // There is no requirement about how the cells are stored beyond that // form a dense submatrix of a larger dense matrix. Like everywhere @@ -77,7 +77,7 @@ namespace internal { // &row, &col, // &row_stride, &col_stride); // -// if (cell != NULL) { +// if (cell != nullptr) { // MatrixRef m(cell->values, row_stride, col_stride); // std::lock_guard<std::mutex> l(&cell->m); // m.block(row, col, row_block_size, col_block_size) = ... @@ -85,21 +85,21 @@ namespace internal { // Structure to carry a pointer to the array containing a cell and the // mutex guarding it. -struct CellInfo { - CellInfo() : values(nullptr) {} +struct CERES_NO_EXPORT CellInfo { + CellInfo() = default; explicit CellInfo(double* values) : values(values) {} - double* values; + double* values{nullptr}; std::mutex m; }; -class CERES_EXPORT_INTERNAL BlockRandomAccessMatrix { +class CERES_NO_EXPORT BlockRandomAccessMatrix { public: virtual ~BlockRandomAccessMatrix(); // If the cell (row_block_id, col_block_id) is present, then return // a CellInfo with a pointer to the dense matrix containing it, - // otherwise return NULL. The dense matrix containing this cell has + // otherwise return nullptr. The dense matrix containing this cell has // size row_stride, col_stride and the cell is located at position // (row, col) within this matrix. // diff --git a/extern/ceres/internal/ceres/block_random_access_sparse_matrix.cc b/extern/ceres/internal/ceres/block_random_access_sparse_matrix.cc index c28b7cef3f4..a026daa5dac 100644 --- a/extern/ceres/internal/ceres/block_random_access_sparse_matrix.cc +++ b/extern/ceres/internal/ceres/block_random_access_sparse_matrix.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -36,7 +36,7 @@ #include <utility> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/triplet_sparse_matrix.h" #include "ceres/types.h" #include "glog/logging.h" @@ -58,9 +58,9 @@ BlockRandomAccessSparseMatrix::BlockRandomAccessSparseMatrix( // rows/columns. int num_cols = 0; block_positions_.reserve(blocks_.size()); - for (int i = 0; i < blocks_.size(); ++i) { + for (int block_size : blocks_) { block_positions_.push_back(num_cols); - num_cols += blocks_[i]; + num_cols += block_size; } // Count the number of scalar non-zero entries and build the layout @@ -76,7 +76,8 @@ BlockRandomAccessSparseMatrix::BlockRandomAccessSparseMatrix( VLOG(1) << "Matrix Size [" << num_cols << "," << num_cols << "] " << num_nonzeros; - tsm_.reset(new TripletSparseMatrix(num_cols, num_cols, num_nonzeros)); + tsm_ = + std::make_unique<TripletSparseMatrix>(num_cols, num_cols, num_nonzeros); tsm_->set_num_nonzeros(num_nonzeros); int* rows = tsm_->mutable_rows(); int* cols = tsm_->mutable_cols(); @@ -86,7 +87,7 @@ BlockRandomAccessSparseMatrix::BlockRandomAccessSparseMatrix( for (const auto& block_pair : block_pairs) { const int row_block_size = blocks_[block_pair.first]; const int col_block_size = blocks_[block_pair.second]; - cell_values_.push_back(make_pair(block_pair, values + pos)); + cell_values_.emplace_back(block_pair, values + pos); layout_[IntPairToLong(block_pair.first, block_pair.second)] = new CellInfo(values + pos); pos += row_block_size * col_block_size; @@ -129,7 +130,7 @@ CellInfo* BlockRandomAccessSparseMatrix::GetCell(int row_block_id, const LayoutType::iterator it = layout_.find(IntPairToLong(row_block_id, col_block_id)); if (it == layout_.end()) { - return NULL; + return nullptr; } // Each cell is stored contiguously as its own little dense matrix. diff --git a/extern/ceres/internal/ceres/block_random_access_sparse_matrix.h b/extern/ceres/internal/ceres/block_random_access_sparse_matrix.h index 0e58bbb6b42..b31a2ade843 100644 --- a/extern/ceres/internal/ceres/block_random_access_sparse_matrix.h +++ b/extern/ceres/internal/ceres/block_random_access_sparse_matrix.h @@ -39,7 +39,8 @@ #include <vector> #include "ceres/block_random_access_matrix.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/small_blas.h" #include "ceres/triplet_sparse_matrix.h" #include "ceres/types.h" @@ -51,7 +52,7 @@ namespace internal { // BlockRandomAccessMatrix. Internally a TripletSparseMatrix is used // for doing the actual storage. This class augments this matrix with // an unordered_map that allows random read/write access. -class CERES_EXPORT_INTERNAL BlockRandomAccessSparseMatrix +class CERES_NO_EXPORT BlockRandomAccessSparseMatrix : public BlockRandomAccessMatrix { public: // blocks is an array of block sizes. block_pairs is a set of @@ -65,7 +66,7 @@ class CERES_EXPORT_INTERNAL BlockRandomAccessSparseMatrix // The destructor is not thread safe. It assumes that no one is // modifying any cells when the matrix is being destroyed. - virtual ~BlockRandomAccessSparseMatrix(); + ~BlockRandomAccessSparseMatrix() override; // BlockRandomAccessMatrix Interface. CellInfo* GetCell(int row_block_id, @@ -111,7 +112,7 @@ class CERES_EXPORT_INTERNAL BlockRandomAccessSparseMatrix // A mapping from <row_block_id, col_block_id> to the position in // the values array of tsm_ where the block is stored. - typedef std::unordered_map<long int, CellInfo*> LayoutType; + using LayoutType = std::unordered_map<long, CellInfo*>; LayoutType layout_; // In order traversal of contents of the matrix. This allows us to @@ -127,4 +128,6 @@ class CERES_EXPORT_INTERNAL BlockRandomAccessSparseMatrix } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_BLOCK_RANDOM_ACCESS_SPARSE_MATRIX_H_ diff --git a/extern/ceres/internal/ceres/block_sparse_matrix.cc b/extern/ceres/internal/ceres/block_sparse_matrix.cc index 5efd2e1ecfb..31ea39daeea 100644 --- a/extern/ceres/internal/ceres/block_sparse_matrix.cc +++ b/extern/ceres/internal/ceres/block_sparse_matrix.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -32,6 +32,7 @@ #include <algorithm> #include <cstddef> +#include <memory> #include <vector> #include "ceres/block_structure.h" @@ -46,8 +47,6 @@ namespace internal { using std::vector; -BlockSparseMatrix::~BlockSparseMatrix() {} - BlockSparseMatrix::BlockSparseMatrix( CompressedRowBlockStructure* block_structure) : num_rows_(0), @@ -57,8 +56,8 @@ BlockSparseMatrix::BlockSparseMatrix( CHECK(block_structure_ != nullptr); // Count the number of columns in the matrix. - for (int i = 0; i < block_structure_->cols.size(); ++i) { - num_cols_ += block_structure_->cols[i].size; + for (auto& col : block_structure_->cols) { + num_cols_ += col.size; } // Count the number of non-zero entries and the number of rows in @@ -68,8 +67,8 @@ BlockSparseMatrix::BlockSparseMatrix( num_rows_ += row_block_size; const vector<Cell>& cells = block_structure_->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - int col_block_id = cells[j].block_id; + for (const auto& cell : cells) { + int col_block_id = cell.block_id; int col_block_size = block_structure_->cols[col_block_id].size; num_nonzeros_ += col_block_size * row_block_size; } @@ -80,7 +79,7 @@ BlockSparseMatrix::BlockSparseMatrix( CHECK_GE(num_nonzeros_, 0); VLOG(2) << "Allocating values array with " << num_nonzeros_ * sizeof(double) << " bytes."; // NOLINT - values_.reset(new double[num_nonzeros_]); + values_ = std::make_unique<double[]>(num_nonzeros_); max_num_nonzeros_ = num_nonzeros_; CHECK(values_ != nullptr); } @@ -97,12 +96,12 @@ void BlockSparseMatrix::RightMultiply(const double* x, double* y) const { int row_block_pos = block_structure_->rows[i].block.position; int row_block_size = block_structure_->rows[i].block.size; const vector<Cell>& cells = block_structure_->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - int col_block_id = cells[j].block_id; + for (const auto& cell : cells) { + int col_block_id = cell.block_id; int col_block_size = block_structure_->cols[col_block_id].size; int col_block_pos = block_structure_->cols[col_block_id].position; MatrixVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>( - values_.get() + cells[j].position, + values_.get() + cell.position, row_block_size, col_block_size, x + col_block_pos, @@ -119,12 +118,12 @@ void BlockSparseMatrix::LeftMultiply(const double* x, double* y) const { int row_block_pos = block_structure_->rows[i].block.position; int row_block_size = block_structure_->rows[i].block.size; const vector<Cell>& cells = block_structure_->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - int col_block_id = cells[j].block_id; + for (const auto& cell : cells) { + int col_block_id = cell.block_id; int col_block_size = block_structure_->cols[col_block_id].size; int col_block_pos = block_structure_->cols[col_block_id].position; MatrixTransposeVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>( - values_.get() + cells[j].position, + values_.get() + cell.position, row_block_size, col_block_size, x + row_block_pos, @@ -139,12 +138,12 @@ void BlockSparseMatrix::SquaredColumnNorm(double* x) const { for (int i = 0; i < block_structure_->rows.size(); ++i) { int row_block_size = block_structure_->rows[i].block.size; const vector<Cell>& cells = block_structure_->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - int col_block_id = cells[j].block_id; + for (const auto& cell : cells) { + int col_block_id = cell.block_id; int col_block_size = block_structure_->cols[col_block_id].size; int col_block_pos = block_structure_->cols[col_block_id].position; const MatrixRef m( - values_.get() + cells[j].position, row_block_size, col_block_size); + values_.get() + cell.position, row_block_size, col_block_size); VectorRef(x + col_block_pos, col_block_size) += m.colwise().squaredNorm(); } } @@ -156,12 +155,12 @@ void BlockSparseMatrix::ScaleColumns(const double* scale) { for (int i = 0; i < block_structure_->rows.size(); ++i) { int row_block_size = block_structure_->rows[i].block.size; const vector<Cell>& cells = block_structure_->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - int col_block_id = cells[j].block_id; + for (const auto& cell : cells) { + int col_block_id = cell.block_id; int col_block_size = block_structure_->cols[col_block_id].size; int col_block_pos = block_structure_->cols[col_block_id].position; MatrixRef m( - values_.get() + cells[j].position, row_block_size, col_block_size); + values_.get() + cell.position, row_block_size, col_block_size); m *= ConstVectorRef(scale + col_block_pos, col_block_size).asDiagonal(); } } @@ -178,11 +177,11 @@ void BlockSparseMatrix::ToDenseMatrix(Matrix* dense_matrix) const { int row_block_pos = block_structure_->rows[i].block.position; int row_block_size = block_structure_->rows[i].block.size; const vector<Cell>& cells = block_structure_->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - int col_block_id = cells[j].block_id; + for (const auto& cell : cells) { + int col_block_id = cell.block_id; int col_block_size = block_structure_->cols[col_block_id].size; int col_block_pos = block_structure_->cols[col_block_id].position; - int jac_pos = cells[j].position; + int jac_pos = cell.position; m.block(row_block_pos, col_block_pos, row_block_size, col_block_size) += MatrixRef(values_.get() + jac_pos, row_block_size, col_block_size); } @@ -201,11 +200,11 @@ void BlockSparseMatrix::ToTripletSparseMatrix( int row_block_pos = block_structure_->rows[i].block.position; int row_block_size = block_structure_->rows[i].block.size; const vector<Cell>& cells = block_structure_->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - int col_block_id = cells[j].block_id; + for (const auto& cell : cells) { + int col_block_id = cell.block_id; int col_block_size = block_structure_->cols[col_block_id].size; int col_block_pos = block_structure_->cols[col_block_id].position; - int jac_pos = cells[j].position; + int jac_pos = cell.position; for (int r = 0; r < row_block_size; ++r) { for (int c = 0; c < col_block_size; ++c, ++jac_pos) { matrix->mutable_rows()[jac_pos] = row_block_pos + r; @@ -230,11 +229,11 @@ void BlockSparseMatrix::ToTextFile(FILE* file) const { const int row_block_pos = block_structure_->rows[i].block.position; const int row_block_size = block_structure_->rows[i].block.size; const vector<Cell>& cells = block_structure_->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - const int col_block_id = cells[j].block_id; + for (const auto& cell : cells) { + const int col_block_id = cell.block_id; const int col_block_size = block_structure_->cols[col_block_id].size; const int col_block_pos = block_structure_->cols[col_block_id].position; - int jac_pos = cells[j].position; + int jac_pos = cell.position; for (int r = 0; r < row_block_size; ++r) { for (int c = 0; c < col_block_size; ++c) { fprintf(file, @@ -248,10 +247,10 @@ void BlockSparseMatrix::ToTextFile(FILE* file) const { } } -BlockSparseMatrix* BlockSparseMatrix::CreateDiagonalMatrix( +std::unique_ptr<BlockSparseMatrix> BlockSparseMatrix::CreateDiagonalMatrix( const double* diagonal, const std::vector<Block>& column_blocks) { // Create the block structure for the diagonal matrix. - CompressedRowBlockStructure* bs = new CompressedRowBlockStructure(); + auto* bs = new CompressedRowBlockStructure(); bs->cols = column_blocks; int position = 0; bs->rows.resize(column_blocks.size(), CompressedRow(1)); @@ -265,13 +264,13 @@ BlockSparseMatrix* BlockSparseMatrix::CreateDiagonalMatrix( } // Create the BlockSparseMatrix with the given block structure. - BlockSparseMatrix* matrix = new BlockSparseMatrix(bs); + auto matrix = std::make_unique<BlockSparseMatrix>(bs); matrix->SetZero(); // Fill the values array of the block sparse matrix. double* values = matrix->mutable_values(); - for (int i = 0; i < column_blocks.size(); ++i) { - const int size = column_blocks[i].size; + for (const auto& column_block : column_blocks) { + const int size = column_block.size; for (int j = 0; j < size; ++j) { // (j + 1) * size is compact way of accessing the (j,j) entry. values[j * (size + 1)] = diagonal[j]; @@ -308,9 +307,10 @@ void BlockSparseMatrix::AppendRows(const BlockSparseMatrix& m) { } if (num_nonzeros_ > max_num_nonzeros_) { - double* new_values = new double[num_nonzeros_]; - std::copy(values_.get(), values_.get() + old_num_nonzeros, new_values); - values_.reset(new_values); + std::unique_ptr<double[]> new_values = + std::make_unique<double[]>(num_nonzeros_); + std::copy_n(values_.get(), old_num_nonzeros, new_values.get()); + values_ = std::move(new_values); max_num_nonzeros_ = num_nonzeros_; } @@ -337,7 +337,7 @@ void BlockSparseMatrix::DeleteRowBlocks(const int delta_row_blocks) { block_structure_->rows.resize(num_row_blocks - delta_row_blocks); } -BlockSparseMatrix* BlockSparseMatrix::CreateRandomMatrix( +std::unique_ptr<BlockSparseMatrix> BlockSparseMatrix::CreateRandomMatrix( const BlockSparseMatrix::RandomMatrixOptions& options) { CHECK_GT(options.num_row_blocks, 0); CHECK_GT(options.min_row_block_size, 0); @@ -346,7 +346,7 @@ BlockSparseMatrix* BlockSparseMatrix::CreateRandomMatrix( CHECK_GT(options.block_density, 0.0); CHECK_LE(options.block_density, 1.0); - CompressedRowBlockStructure* bs = new CompressedRowBlockStructure(); + auto* bs = new CompressedRowBlockStructure(); if (options.col_blocks.empty()) { CHECK_GT(options.num_col_blocks, 0); CHECK_GT(options.min_col_block_size, 0); @@ -360,7 +360,7 @@ BlockSparseMatrix* BlockSparseMatrix::CreateRandomMatrix( const int delta_block_size = Uniform(options.max_col_block_size - options.min_col_block_size); const int col_block_size = options.min_col_block_size + delta_block_size; - bs->cols.push_back(Block(col_block_size, col_block_position)); + bs->cols.emplace_back(col_block_size, col_block_position); col_block_position += col_block_size; } } else { @@ -377,7 +377,7 @@ BlockSparseMatrix* BlockSparseMatrix::CreateRandomMatrix( const int delta_block_size = Uniform(options.max_row_block_size - options.min_row_block_size); const int row_block_size = options.min_row_block_size + delta_block_size; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = row_block_size; row.block.position = row_block_position; @@ -385,7 +385,7 @@ BlockSparseMatrix* BlockSparseMatrix::CreateRandomMatrix( for (int c = 0; c < bs->cols.size(); ++c) { if (RandDouble() > options.block_density) continue; - row.cells.push_back(Cell()); + row.cells.emplace_back(); Cell& cell = row.cells.back(); cell.block_id = c; cell.position = value_position; @@ -395,7 +395,7 @@ BlockSparseMatrix* BlockSparseMatrix::CreateRandomMatrix( } } - BlockSparseMatrix* matrix = new BlockSparseMatrix(bs); + auto matrix = std::make_unique<BlockSparseMatrix>(bs); double* values = matrix->mutable_values(); for (int i = 0; i < matrix->num_nonzeros(); ++i) { values[i] = RandNormal(); diff --git a/extern/ceres/internal/ceres/block_sparse_matrix.h b/extern/ceres/internal/ceres/block_sparse_matrix.h index e5b3634c3cc..75b0deb59e6 100644 --- a/extern/ceres/internal/ceres/block_sparse_matrix.h +++ b/extern/ceres/internal/ceres/block_sparse_matrix.h @@ -37,8 +37,9 @@ #include <memory> #include "ceres/block_structure.h" +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/sparse_matrix.h" namespace ceres { @@ -54,7 +55,7 @@ class TripletSparseMatrix; // // internal/ceres/block_structure.h // -class CERES_EXPORT_INTERNAL BlockSparseMatrix : public SparseMatrix { +class CERES_NO_EXPORT BlockSparseMatrix final : public SparseMatrix { public: // Construct a block sparse matrix with a fully initialized // CompressedRowBlockStructure objected. The matrix takes over @@ -68,8 +69,6 @@ class CERES_EXPORT_INTERNAL BlockSparseMatrix : public SparseMatrix { BlockSparseMatrix(const BlockSparseMatrix&) = delete; void operator=(const BlockSparseMatrix&) = delete; - virtual ~BlockSparseMatrix(); - // Implementation of SparseMatrix interface. void SetZero() final; void RightMultiply(const double* x, double* y) const final; @@ -97,7 +96,7 @@ class CERES_EXPORT_INTERNAL BlockSparseMatrix : public SparseMatrix { // Delete the bottom delta_rows_blocks. void DeleteRowBlocks(int delta_row_blocks); - static BlockSparseMatrix* CreateDiagonalMatrix( + static std::unique_ptr<BlockSparseMatrix> CreateDiagonalMatrix( const double* diagonal, const std::vector<Block>& column_blocks); struct RandomMatrixOptions { @@ -122,9 +121,7 @@ class CERES_EXPORT_INTERNAL BlockSparseMatrix : public SparseMatrix { // Create a random BlockSparseMatrix whose entries are normally // distributed and whose structure is determined by // RandomMatrixOptions. - // - // Caller owns the result. - static BlockSparseMatrix* CreateRandomMatrix( + static std::unique_ptr<BlockSparseMatrix> CreateRandomMatrix( const RandomMatrixOptions& options); private: @@ -142,9 +139,9 @@ class CERES_EXPORT_INTERNAL BlockSparseMatrix : public SparseMatrix { // // BlockSparseDataMatrix a struct that carries these two bits of // information -class BlockSparseMatrixData { +class CERES_NO_EXPORT BlockSparseMatrixData { public: - BlockSparseMatrixData(const BlockSparseMatrix& m) + explicit BlockSparseMatrixData(const BlockSparseMatrix& m) : block_structure_(m.block_structure()), values_(m.values()){}; BlockSparseMatrixData(const CompressedRowBlockStructure* block_structure, @@ -164,4 +161,6 @@ class BlockSparseMatrixData { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_BLOCK_SPARSE_MATRIX_H_ diff --git a/extern/ceres/internal/ceres/block_structure.h b/extern/ceres/internal/ceres/block_structure.h index d49d7d3f3a4..fe7574c6817 100644 --- a/extern/ceres/internal/ceres/block_structure.h +++ b/extern/ceres/internal/ceres/block_structure.h @@ -41,54 +41,54 @@ #include <cstdint> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { -typedef int32_t BlockSize; +using BlockSize = int32_t; -struct Block { - Block() : size(-1), position(-1) {} +struct CERES_NO_EXPORT Block { + Block() = default; Block(int size_, int position_) : size(size_), position(position_) {} - BlockSize size; - int position; // Position along the row/column. + BlockSize size{-1}; + int position{-1}; // Position along the row/column. }; -struct Cell { - Cell() : block_id(-1), position(-1) {} +struct CERES_NO_EXPORT Cell { + Cell() = default; Cell(int block_id_, int position_) : block_id(block_id_), position(position_) {} // Column or row block id as the case maybe. - int block_id; + int block_id{-1}; // Where in the values array of the jacobian is this cell located. - int position; + int position{-1}; }; // Order cell by their block_id; -bool CellLessThan(const Cell& lhs, const Cell& rhs); +CERES_NO_EXPORT bool CellLessThan(const Cell& lhs, const Cell& rhs); -struct CompressedList { - CompressedList() {} +struct CERES_NO_EXPORT CompressedList { + CompressedList() = default; // Construct a CompressedList with the cells containing num_cells // entries. - CompressedList(int num_cells) : cells(num_cells) {} + explicit CompressedList(int num_cells) : cells(num_cells) {} Block block; std::vector<Cell> cells; }; -typedef CompressedList CompressedRow; -typedef CompressedList CompressedColumn; +using CompressedRow = CompressedList; +using CompressedColumn = CompressedList; -struct CompressedRowBlockStructure { +struct CERES_NO_EXPORT CompressedRowBlockStructure { std::vector<Block> cols; std::vector<CompressedRow> rows; }; -struct CompressedColumnBlockStructure { +struct CERES_NO_EXPORT CompressedColumnBlockStructure { std::vector<Block> rows; std::vector<CompressedColumn> cols; }; diff --git a/extern/ceres/internal/ceres/c_api.cc b/extern/ceres/internal/ceres/c_api.cc index 251cde42101..8ea344dd54a 100644 --- a/extern/ceres/internal/ceres/c_api.cc +++ b/extern/ceres/internal/ceres/c_api.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -35,6 +35,7 @@ #include "ceres/c_api.h" #include <iostream> +#include <memory> #include <string> #include <vector> @@ -64,7 +65,7 @@ void ceres_free_problem(ceres_problem_t* problem) { // This cost function wraps a C-level function pointer from the user, to bridge // between C and C++. -class CallbackCostFunction : public ceres::CostFunction { +class CERES_NO_EXPORT CallbackCostFunction final : public ceres::CostFunction { public: CallbackCostFunction(ceres_cost_function_t cost_function, void* user_data, @@ -78,8 +79,6 @@ class CallbackCostFunction : public ceres::CostFunction { } } - virtual ~CallbackCostFunction() {} - bool Evaluate(double const* const* parameters, double* residuals, double** jacobians) const final { @@ -94,7 +93,7 @@ class CallbackCostFunction : public ceres::CostFunction { // This loss function wraps a C-level function pointer from the user, to bridge // between C and C++. -class CallbackLossFunction : public ceres::LossFunction { +class CallbackLossFunction final : public ceres::LossFunction { public: explicit CallbackLossFunction(ceres_loss_function_t loss_function, void* user_data) @@ -146,30 +145,31 @@ ceres_residual_block_id_t* ceres_problem_add_residual_block( int num_parameter_blocks, int* parameter_block_sizes, double** parameters) { - Problem* ceres_problem = reinterpret_cast<Problem*>(problem); - - ceres::CostFunction* callback_cost_function = - new CallbackCostFunction(cost_function, - cost_function_data, - num_residuals, - num_parameter_blocks, - parameter_block_sizes); - - ceres::LossFunction* callback_loss_function = NULL; - if (loss_function != NULL) { - callback_loss_function = - new CallbackLossFunction(loss_function, loss_function_data); + auto* ceres_problem = reinterpret_cast<Problem*>(problem); + + auto callback_cost_function = + std::make_unique<CallbackCostFunction>(cost_function, + cost_function_data, + num_residuals, + num_parameter_blocks, + parameter_block_sizes); + + std::unique_ptr<ceres::LossFunction> callback_loss_function; + if (loss_function != nullptr) { + callback_loss_function = std::make_unique<CallbackLossFunction>( + loss_function, loss_function_data); } std::vector<double*> parameter_blocks(parameters, parameters + num_parameter_blocks); return reinterpret_cast<ceres_residual_block_id_t*>( - ceres_problem->AddResidualBlock( - callback_cost_function, callback_loss_function, parameter_blocks)); + ceres_problem->AddResidualBlock(callback_cost_function.release(), + callback_loss_function.release(), + parameter_blocks)); } void ceres_solve(ceres_problem_t* c_problem) { - Problem* problem = reinterpret_cast<Problem*>(c_problem); + auto* problem = reinterpret_cast<Problem*>(c_problem); // TODO(keir): Obviously, this way of setting options won't scale or last. // Instead, figure out a way to specify some of the options without diff --git a/extern/ceres/internal/ceres/callbacks.cc b/extern/ceres/internal/ceres/callbacks.cc index 0e0df9d91b1..7a4381c293f 100644 --- a/extern/ceres/internal/ceres/callbacks.cc +++ b/extern/ceres/internal/ceres/callbacks.cc @@ -30,6 +30,7 @@ #include "ceres/callbacks.h" +#include <algorithm> #include <iostream> // NO LINT #include "ceres/program.h" @@ -45,7 +46,7 @@ StateUpdatingCallback::StateUpdatingCallback(Program* program, double* parameters) : program_(program), parameters_(parameters) {} -StateUpdatingCallback::~StateUpdatingCallback() {} +StateUpdatingCallback::~StateUpdatingCallback() = default; CallbackReturnType StateUpdatingCallback::operator()( const IterationSummary& summary) { @@ -64,14 +65,12 @@ GradientProblemSolverStateUpdatingCallback:: user_parameters_(user_parameters) {} GradientProblemSolverStateUpdatingCallback:: - ~GradientProblemSolverStateUpdatingCallback() {} + ~GradientProblemSolverStateUpdatingCallback() = default; CallbackReturnType GradientProblemSolverStateUpdatingCallback::operator()( const IterationSummary& summary) { if (summary.step_is_successful) { - std::copy(internal_parameters_, - internal_parameters_ + num_parameters_, - user_parameters_); + std::copy_n(internal_parameters_, num_parameters_, user_parameters_); } return SOLVER_CONTINUE; } @@ -80,44 +79,42 @@ LoggingCallback::LoggingCallback(const MinimizerType minimizer_type, const bool log_to_stdout) : minimizer_type(minimizer_type), log_to_stdout_(log_to_stdout) {} -LoggingCallback::~LoggingCallback() {} +LoggingCallback::~LoggingCallback() = default; CallbackReturnType LoggingCallback::operator()( const IterationSummary& summary) { string output; if (minimizer_type == LINE_SEARCH) { - const char* kReportRowFormat = - "% 4d: f:% 8e d:% 3.2e g:% 3.2e h:% 3.2e " - "s:% 3.2e e:% 3d it:% 3.2e tt:% 3.2e"; - output = StringPrintf(kReportRowFormat, - summary.iteration, - summary.cost, - summary.cost_change, - summary.gradient_max_norm, - summary.step_norm, - summary.step_size, - summary.line_search_function_evaluations, - summary.iteration_time_in_seconds, - summary.cumulative_time_in_seconds); + output = StringPrintf( + "% 4d: f:% 8e d:% 3.2e g:% 3.2e h:% 3.2e s:% 3.2e e:% 3d it:% 3.2e " + "tt:% 3.2e", + summary.iteration, + summary.cost, + summary.cost_change, + summary.gradient_max_norm, + summary.step_norm, + summary.step_size, + summary.line_search_function_evaluations, + summary.iteration_time_in_seconds, + summary.cumulative_time_in_seconds); } else if (minimizer_type == TRUST_REGION) { // clang-format off if (summary.iteration == 0) { output = "iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time\n"; // NOLINT } - const char* kReportRowFormat = - "% 4d % 8e % 3.2e % 3.2e % 3.2e % 3.2e % 3.2e % 4d % 3.2e % 3.2e"; // NOLINT - // clang-format on - output += StringPrintf(kReportRowFormat, - summary.iteration, - summary.cost, - summary.cost_change, - summary.gradient_max_norm, - summary.step_norm, - summary.relative_decrease, - summary.trust_region_radius, - summary.linear_solver_iterations, - summary.iteration_time_in_seconds, - summary.cumulative_time_in_seconds); + output += StringPrintf( + "% 4d % 8e % 3.2e % 3.2e % 3.2e % 3.2e % 3.2e % 4d % 3.2e % 3.2e", // NOLINT + // clang-format on + summary.iteration, + summary.cost, + summary.cost_change, + summary.gradient_max_norm, + summary.step_norm, + summary.relative_decrease, + summary.trust_region_radius, + summary.linear_solver_iterations, + summary.iteration_time_in_seconds, + summary.cumulative_time_in_seconds); } else { LOG(FATAL) << "Unknown minimizer type."; } diff --git a/extern/ceres/internal/ceres/callbacks.h b/extern/ceres/internal/ceres/callbacks.h index 47112b88fd8..3b1d10cfa7f 100644 --- a/extern/ceres/internal/ceres/callbacks.h +++ b/extern/ceres/internal/ceres/callbacks.h @@ -33,7 +33,7 @@ #include <string> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/iteration_callback.h" namespace ceres { @@ -43,10 +43,10 @@ class Program; // Callback for updating the externally visible state of parameter // blocks. -class StateUpdatingCallback : public IterationCallback { +class CERES_NO_EXPORT StateUpdatingCallback final : public IterationCallback { public: StateUpdatingCallback(Program* program, double* parameters); - virtual ~StateUpdatingCallback(); + ~StateUpdatingCallback() override; CallbackReturnType operator()(const IterationSummary& summary) final; private: @@ -56,12 +56,13 @@ class StateUpdatingCallback : public IterationCallback { // Callback for updating the externally visible state of the // parameters vector for GradientProblemSolver. -class GradientProblemSolverStateUpdatingCallback : public IterationCallback { +class CERES_NO_EXPORT GradientProblemSolverStateUpdatingCallback final + : public IterationCallback { public: GradientProblemSolverStateUpdatingCallback(int num_parameters, const double* internal_parameters, double* user_parameters); - virtual ~GradientProblemSolverStateUpdatingCallback(); + ~GradientProblemSolverStateUpdatingCallback() override; CallbackReturnType operator()(const IterationSummary& summary) final; private: @@ -72,10 +73,10 @@ class GradientProblemSolverStateUpdatingCallback : public IterationCallback { // Callback for logging the state of the minimizer to STDERR or // STDOUT depending on the user's preferences and logging level. -class LoggingCallback : public IterationCallback { +class CERES_NO_EXPORT LoggingCallback final : public IterationCallback { public: LoggingCallback(MinimizerType minimizer_type, bool log_to_stdout); - virtual ~LoggingCallback(); + ~LoggingCallback() override; CallbackReturnType operator()(const IterationSummary& summary) final; private: diff --git a/extern/ceres/internal/ceres/canonical_views_clustering.cc b/extern/ceres/internal/ceres/canonical_views_clustering.cc index c193735f106..01b8ad38ae0 100644 --- a/extern/ceres/internal/ceres/canonical_views_clustering.cc +++ b/extern/ceres/internal/ceres/canonical_views_clustering.cc @@ -35,6 +35,7 @@ #include <unordered_set> #include "ceres/graph.h" +#include "ceres/internal/export.h" #include "ceres/map_util.h" #include "glog/logging.h" @@ -43,13 +44,11 @@ namespace internal { using std::vector; -typedef std::unordered_map<int, int> IntMap; -typedef std::unordered_set<int> IntSet; +using IntMap = std::unordered_map<int, int>; +using IntSet = std::unordered_set<int>; -class CanonicalViewsClustering { +class CERES_NO_EXPORT CanonicalViewsClustering { public: - CanonicalViewsClustering() {} - // Compute the canonical views clustering of the vertices of the // graph. centers will contain the vertices that are the identified // as the canonical views/cluster centers, and membership is a map @@ -85,11 +84,11 @@ void ComputeCanonicalViewsClustering( const WeightedGraph<int>& graph, vector<int>* centers, IntMap* membership) { - time_t start_time = time(NULL); + time_t start_time = time(nullptr); CanonicalViewsClustering cv; cv.ComputeClustering(options, graph, centers, membership); VLOG(2) << "Canonical views clustering time (secs): " - << time(NULL) - start_time; + << time(nullptr) - start_time; } // Implementation of CanonicalViewsClustering @@ -107,7 +106,7 @@ void CanonicalViewsClustering::ComputeClustering( IntSet valid_views; FindValidViews(&valid_views); - while (valid_views.size() > 0) { + while (!valid_views.empty()) { // Find the next best canonical view. double best_difference = -std::numeric_limits<double>::max(); int best_view = 0; @@ -174,9 +173,9 @@ double CanonicalViewsClustering::ComputeClusteringQualityDifference( difference -= options_.size_penalty_weight; // Orthogonality. - for (int i = 0; i < centers.size(); ++i) { + for (int center : centers) { difference -= options_.similarity_penalty_weight * - graph_->EdgeWeight(centers[i], candidate); + graph_->EdgeWeight(center, candidate); } return difference; diff --git a/extern/ceres/internal/ceres/canonical_views_clustering.h b/extern/ceres/internal/ceres/canonical_views_clustering.h index 465233ddfcd..00a6a739d29 100644 --- a/extern/ceres/internal/ceres/canonical_views_clustering.h +++ b/extern/ceres/internal/ceres/canonical_views_clustering.h @@ -45,7 +45,8 @@ #include <vector> #include "ceres/graph.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -95,13 +96,13 @@ struct CanonicalViewsClusteringOptions; // It is possible depending on the configuration of the clustering // algorithm that some of the vertices may not be assigned to any // cluster. In this case they are assigned to a cluster with id = -1; -CERES_EXPORT_INTERNAL void ComputeCanonicalViewsClustering( +CERES_NO_EXPORT void ComputeCanonicalViewsClustering( const CanonicalViewsClusteringOptions& options, const WeightedGraph<int>& graph, std::vector<int>* centers, std::unordered_map<int, int>* membership); -struct CERES_EXPORT_INTERNAL CanonicalViewsClusteringOptions { +struct CERES_NO_EXPORT CanonicalViewsClusteringOptions { // The minimum number of canonical views to compute. int min_views = 3; @@ -122,4 +123,6 @@ struct CERES_EXPORT_INTERNAL CanonicalViewsClusteringOptions { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_CANONICAL_VIEWS_CLUSTERING_H_ diff --git a/extern/ceres/internal/ceres/casts.h b/extern/ceres/internal/ceres/casts.h index d13707131c2..04d8ba4fe33 100644 --- a/extern/ceres/internal/ceres/casts.h +++ b/extern/ceres/internal/ceres/casts.h @@ -32,14 +32,13 @@ #define CERES_INTERNAL_CASTS_H_ #include <cassert> -#include <cstddef> // For NULL. namespace ceres { // Identity metafunction. template <class T> struct identity_ { - typedef T type; + using type = T; }; // Use implicit_cast as a safe version of static_cast or const_cast @@ -86,6 +85,7 @@ inline To implicit_cast(typename identity_<From>::type const& f) { // if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo); // You should design the code some other way not to need this. +// TODO(sameeragarwal): Modernize this. template <typename To, typename From> // use like this: down_cast<T*>(foo); inline To down_cast(From* f) { // so we only accept pointers // Ensures that To is a sub-type of From *. This test is here only @@ -95,11 +95,11 @@ inline To down_cast(From* f) { // so we only accept pointers // TODO(csilvers): This should use COMPILE_ASSERT. if (false) { - implicit_cast<From*, To>(NULL); + implicit_cast<From*, To>(nullptr); } // uses RTTI in dbg and fastbuild. asserts are disabled in opt builds. - assert(f == NULL || dynamic_cast<To>(f) != NULL); // NOLINT + assert(f == nullptr || dynamic_cast<To>(f) != nullptr); // NOLINT return static_cast<To>(f); } diff --git a/extern/ceres/internal/ceres/cgnr_linear_operator.h b/extern/ceres/internal/ceres/cgnr_linear_operator.h index beb8bbc2c2a..d708efca24c 100644 --- a/extern/ceres/internal/ceres/cgnr_linear_operator.h +++ b/extern/ceres/internal/ceres/cgnr_linear_operator.h @@ -34,7 +34,9 @@ #include <algorithm> #include <memory> +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" #include "ceres/linear_operator.h" namespace ceres { @@ -78,11 +80,10 @@ class SparseMatrix; // and z = A^T b // // Note: This class is not thread safe, since it uses some temporary storage. -class CgnrLinearOperator : public LinearOperator { +class CERES_NO_EXPORT CgnrLinearOperator final : public LinearOperator { public: CgnrLinearOperator(const LinearOperator& A, const double* D) : A_(A), D_(D), z_(new double[A.num_rows()]) {} - virtual ~CgnrLinearOperator() {} void RightMultiply(const double* x, double* y) const final { std::fill(z_.get(), z_.get() + A_.num_rows(), 0.0); @@ -94,7 +95,7 @@ class CgnrLinearOperator : public LinearOperator { A_.LeftMultiply(z_.get(), y); // y = y + DtDx - if (D_ != NULL) { + if (D_ != nullptr) { int n = A_.num_cols(); VectorRef(y, n).array() += ConstVectorRef(D_, n).array().square() * ConstVectorRef(x, n).array(); @@ -117,4 +118,6 @@ class CgnrLinearOperator : public LinearOperator { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_CGNR_LINEAR_OPERATOR_H_ diff --git a/extern/ceres/internal/ceres/cgnr_solver.cc b/extern/ceres/internal/ceres/cgnr_solver.cc index 9dba1cfb4a8..cca72bca988 100644 --- a/extern/ceres/internal/ceres/cgnr_solver.cc +++ b/extern/ceres/internal/ceres/cgnr_solver.cc @@ -30,6 +30,9 @@ #include "ceres/cgnr_solver.h" +#include <memory> +#include <utility> + #include "ceres/block_jacobi_preconditioner.h" #include "ceres/cgnr_linear_operator.h" #include "ceres/conjugate_gradients_solver.h" @@ -42,8 +45,8 @@ namespace ceres { namespace internal { -CgnrSolver::CgnrSolver(const LinearSolver::Options& options) - : options_(options) { +CgnrSolver::CgnrSolver(LinearSolver::Options options) + : options_(std::move(options)) { if (options_.preconditioner_type != JACOBI && options_.preconditioner_type != IDENTITY && options_.preconditioner_type != SUBSET) { @@ -54,7 +57,7 @@ CgnrSolver::CgnrSolver(const LinearSolver::Options& options) } } -CgnrSolver::~CgnrSolver() {} +CgnrSolver::~CgnrSolver() = default; LinearSolver::Summary CgnrSolver::SolveImpl( BlockSparseMatrix* A, @@ -70,7 +73,7 @@ LinearSolver::Summary CgnrSolver::SolveImpl( if (!preconditioner_) { if (options_.preconditioner_type == JACOBI) { - preconditioner_.reset(new BlockJacobiPreconditioner(*A)); + preconditioner_ = std::make_unique<BlockJacobiPreconditioner>(*A); } else if (options_.preconditioner_type == SUBSET) { Preconditioner::Options preconditioner_options; preconditioner_options.type = SUBSET; @@ -81,8 +84,8 @@ LinearSolver::Summary CgnrSolver::SolveImpl( preconditioner_options.use_postordering = options_.use_postordering; preconditioner_options.num_threads = options_.num_threads; preconditioner_options.context = options_.context; - preconditioner_.reset( - new SubsetPreconditioner(preconditioner_options, *A)); + preconditioner_ = + std::make_unique<SubsetPreconditioner>(preconditioner_options, *A); } } diff --git a/extern/ceres/internal/ceres/cgnr_solver.h b/extern/ceres/internal/ceres/cgnr_solver.h index bc701c0e9ed..25e62e9abd9 100644 --- a/extern/ceres/internal/ceres/cgnr_solver.h +++ b/extern/ceres/internal/ceres/cgnr_solver.h @@ -33,6 +33,7 @@ #include <memory> +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { @@ -49,12 +50,12 @@ class BlockJacobiPreconditioner; // // as required for solving for x in the least squares sense. Currently only // block diagonal preconditioning is supported. -class CgnrSolver : public BlockSparseMatrixSolver { +class CERES_NO_EXPORT CgnrSolver final : public BlockSparseMatrixSolver { public: - explicit CgnrSolver(const LinearSolver::Options& options); + explicit CgnrSolver(LinearSolver::Options options); CgnrSolver(const CgnrSolver&) = delete; void operator=(const CgnrSolver&) = delete; - virtual ~CgnrSolver(); + ~CgnrSolver() override; Summary SolveImpl(BlockSparseMatrix* A, const double* b, diff --git a/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.cc b/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.cc index e1f6bb8ff9d..94e7e9aa446 100644 --- a/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.cc +++ b/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.cc @@ -33,7 +33,7 @@ #include <algorithm> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "glog/logging.h" namespace ceres { diff --git a/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.h b/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.h index d442e1a9bb8..f88a5bd9588 100644 --- a/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.h +++ b/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.h @@ -31,9 +31,11 @@ #ifndef CERES_INTERNAL_COMPRESSED_COL_SPARSE_MATRIX_UTILS_H_ #define CERES_INTERNAL_COMPRESSED_COL_SPARSE_MATRIX_UTILS_H_ +#include <algorithm> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -48,7 +50,7 @@ namespace internal { // and column block j, then it is expected that A contains at least // one non-zero entry corresponding to the top left entry of c_ij, // as that entry is used to detect the presence of a non-zero c_ij. -CERES_EXPORT_INTERNAL void CompressedColumnScalarMatrixToBlockMatrix( +CERES_NO_EXPORT void CompressedColumnScalarMatrixToBlockMatrix( const int* scalar_rows, const int* scalar_cols, const std::vector<int>& row_blocks, @@ -59,7 +61,7 @@ CERES_EXPORT_INTERNAL void CompressedColumnScalarMatrixToBlockMatrix( // Given a set of blocks and a permutation of these blocks, compute // the corresponding "scalar" ordering, where the scalar ordering of // size sum(blocks). -CERES_EXPORT_INTERNAL void BlockOrderingToScalarOrdering( +CERES_NO_EXPORT void BlockOrderingToScalarOrdering( const std::vector<int>& blocks, const std::vector<int>& block_ordering, std::vector<int>* scalar_ordering); @@ -142,4 +144,6 @@ void SolveRTRWithSparseRHS(IntegerType num_cols, } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_COMPRESSED_COL_SPARSE_MATRIX_UTILS_H_ diff --git a/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc b/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc index 8e7e3e7e7e6..55b30a290f9 100644 --- a/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc +++ b/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -30,7 +30,10 @@ #include "ceres/compressed_row_jacobian_writer.h" +#include <algorithm> #include <iterator> +#include <memory> +#include <string> #include <utility> #include <vector> @@ -55,7 +58,7 @@ void CompressedRowJacobianWriter::PopulateJacobianRowAndColumnBlockVectors( vector<int>& col_blocks = *(jacobian->mutable_col_blocks()); col_blocks.resize(parameter_blocks.size()); for (int i = 0; i < parameter_blocks.size(); ++i) { - col_blocks[i] = parameter_blocks[i]->LocalSize(); + col_blocks[i] = parameter_blocks[i]->TangentSize(); } const vector<ResidualBlock*>& residual_blocks = program->residual_blocks(); @@ -81,10 +84,12 @@ void CompressedRowJacobianWriter::GetOrderedParameterBlocks( make_pair(parameter_block->index(), j)); } } - sort(evaluated_jacobian_blocks->begin(), evaluated_jacobian_blocks->end()); + std::sort(evaluated_jacobian_blocks->begin(), + evaluated_jacobian_blocks->end()); } -SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const { +std::unique_ptr<SparseMatrix> CompressedRowJacobianWriter::CreateJacobian() + const { const vector<ResidualBlock*>& residual_blocks = program_->residual_blocks(); int total_num_residuals = program_->NumResiduals(); @@ -92,14 +97,13 @@ SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const { // Count the number of jacobian nonzeros. int num_jacobian_nonzeros = 0; - for (int i = 0; i < residual_blocks.size(); ++i) { - ResidualBlock* residual_block = residual_blocks[i]; + for (auto* residual_block : residual_blocks) { const int num_residuals = residual_block->NumResiduals(); const int num_parameter_blocks = residual_block->NumParameterBlocks(); for (int j = 0; j < num_parameter_blocks; ++j) { ParameterBlock* parameter_block = residual_block->parameter_blocks()[j]; if (!parameter_block->IsConstant()) { - num_jacobian_nonzeros += num_residuals * parameter_block->LocalSize(); + num_jacobian_nonzeros += num_residuals * parameter_block->TangentSize(); } } } @@ -108,10 +112,11 @@ SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const { // Allocate more space than needed to store the jacobian so that when the LM // algorithm adds the diagonal, no reallocation is necessary. This reduces // peak memory usage significantly. - CompressedRowSparseMatrix* jacobian = new CompressedRowSparseMatrix( - total_num_residuals, - total_num_effective_parameters, - num_jacobian_nonzeros + total_num_effective_parameters); + std::unique_ptr<CompressedRowSparseMatrix> jacobian = + std::make_unique<CompressedRowSparseMatrix>( + total_num_residuals, + total_num_effective_parameters, + num_jacobian_nonzeros + total_num_effective_parameters); // At this stage, the CompressedRowSparseMatrix is an invalid state. But this // seems to be the only way to construct it without doing a memory copy. @@ -120,8 +125,7 @@ SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const { int row_pos = 0; rows[0] = 0; - for (int i = 0; i < residual_blocks.size(); ++i) { - const ResidualBlock* residual_block = residual_blocks[i]; + for (auto* residual_block : residual_blocks) { const int num_parameter_blocks = residual_block->NumParameterBlocks(); // Count the number of derivatives for a row of this residual block and @@ -132,7 +136,7 @@ SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const { ParameterBlock* parameter_block = residual_block->parameter_blocks()[j]; if (!parameter_block->IsConstant()) { parameter_indices.push_back(parameter_block->index()); - num_derivatives += parameter_block->LocalSize(); + num_derivatives += parameter_block->TangentSize(); } } @@ -163,10 +167,10 @@ SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const { // parameter vector. This code mirrors that in Write(), where jacobian // values are updated. int col_pos = 0; - for (int j = 0; j < parameter_indices.size(); ++j) { + for (int parameter_index : parameter_indices) { ParameterBlock* parameter_block = - program_->parameter_blocks()[parameter_indices[j]]; - const int parameter_block_size = parameter_block->LocalSize(); + program_->parameter_blocks()[parameter_index]; + const int parameter_block_size = parameter_block->TangentSize(); for (int r = 0; r < num_residuals; ++r) { // This is the position in the values array of the jacobian where this @@ -183,7 +187,7 @@ SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const { } CHECK_EQ(num_jacobian_nonzeros, rows[total_num_residuals]); - PopulateJacobianRowAndColumnBlockVectors(program_, jacobian); + PopulateJacobianRowAndColumnBlockVectors(program_, jacobian.get()); return jacobian; } @@ -192,8 +196,7 @@ void CompressedRowJacobianWriter::Write(int residual_id, int residual_offset, double** jacobians, SparseMatrix* base_jacobian) { - CompressedRowSparseMatrix* jacobian = - down_cast<CompressedRowSparseMatrix*>(base_jacobian); + auto* jacobian = down_cast<CompressedRowSparseMatrix*>(base_jacobian); double* jacobian_values = jacobian->mutable_values(); const int* jacobian_rows = jacobian->rows(); @@ -210,11 +213,11 @@ void CompressedRowJacobianWriter::Write(int residual_id, // Iterate over the jacobian blocks in increasing order of their // positions in the reduced parameter vector. - for (int i = 0; i < evaluated_jacobian_blocks.size(); ++i) { + for (auto& evaluated_jacobian_block : evaluated_jacobian_blocks) { const ParameterBlock* parameter_block = - program_->parameter_blocks()[evaluated_jacobian_blocks[i].first]; - const int argument = evaluated_jacobian_blocks[i].second; - const int parameter_block_size = parameter_block->LocalSize(); + program_->parameter_blocks()[evaluated_jacobian_block.first]; + const int argument = evaluated_jacobian_block.second; + const int parameter_block_size = parameter_block->TangentSize(); // Copy one row of the jacobian block at a time. for (int r = 0; r < num_residuals; ++r) { diff --git a/extern/ceres/internal/ceres/compressed_row_jacobian_writer.h b/extern/ceres/internal/ceres/compressed_row_jacobian_writer.h index b1251ca5cf5..7badab71b04 100644 --- a/extern/ceres/internal/ceres/compressed_row_jacobian_writer.h +++ b/extern/ceres/internal/ceres/compressed_row_jacobian_writer.h @@ -33,10 +33,12 @@ #ifndef CERES_INTERNAL_COMPRESSED_ROW_JACOBIAN_WRITER_H_ #define CERES_INTERNAL_COMPRESSED_ROW_JACOBIAN_WRITER_H_ +#include <memory> #include <utility> #include <vector> #include "ceres/evaluator.h" +#include "ceres/internal/export.h" #include "ceres/scratch_evaluate_preparer.h" namespace ceres { @@ -46,7 +48,7 @@ class CompressedRowSparseMatrix; class Program; class SparseMatrix; -class CompressedRowJacobianWriter { +class CERES_NO_EXPORT CompressedRowJacobianWriter { public: CompressedRowJacobianWriter(Evaluator::Options /* ignored */, Program* program) @@ -89,11 +91,12 @@ class CompressedRowJacobianWriter { // assumed by the cost functions, use scratch space to store the // jacobians temporarily then copy them over to the larger jacobian // in the Write() function. - ScratchEvaluatePreparer* CreateEvaluatePreparers(int num_threads) { + std::unique_ptr<ScratchEvaluatePreparer[]> CreateEvaluatePreparers( + int num_threads) { return ScratchEvaluatePreparer::Create(*program_, num_threads); } - SparseMatrix* CreateJacobian() const; + std::unique_ptr<SparseMatrix> CreateJacobian() const; void Write(int residual_id, int residual_offset, diff --git a/extern/ceres/internal/ceres/compressed_row_sparse_matrix.cc b/extern/ceres/internal/ceres/compressed_row_sparse_matrix.cc index 900586c2c45..db103d9c0fa 100644 --- a/extern/ceres/internal/ceres/compressed_row_sparse_matrix.cc +++ b/extern/ceres/internal/ceres/compressed_row_sparse_matrix.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2017 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -31,11 +31,12 @@ #include "ceres/compressed_row_sparse_matrix.h" #include <algorithm> +#include <memory> #include <numeric> #include <vector> #include "ceres/crs_matrix.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/random.h" #include "ceres/triplet_sparse_matrix.h" #include "glog/logging.h" @@ -104,7 +105,7 @@ void TransposeForCompressedRowSparseStructure(const int num_rows, const int c = cols[idx]; const int transpose_idx = transpose_rows[c]++; transpose_cols[transpose_idx] = r; - if (values != NULL && transpose_values != NULL) { + if (values != nullptr && transpose_values != nullptr) { transpose_values[transpose_idx] = values[idx]; } } @@ -174,18 +175,20 @@ CompressedRowSparseMatrix::CompressedRowSparseMatrix(int num_rows, cols_.size() * sizeof(double); // NOLINT } -CompressedRowSparseMatrix* CompressedRowSparseMatrix::FromTripletSparseMatrix( +std::unique_ptr<CompressedRowSparseMatrix> +CompressedRowSparseMatrix::FromTripletSparseMatrix( const TripletSparseMatrix& input) { return CompressedRowSparseMatrix::FromTripletSparseMatrix(input, false); } -CompressedRowSparseMatrix* +std::unique_ptr<CompressedRowSparseMatrix> CompressedRowSparseMatrix::FromTripletSparseMatrixTransposed( const TripletSparseMatrix& input) { return CompressedRowSparseMatrix::FromTripletSparseMatrix(input, true); } -CompressedRowSparseMatrix* CompressedRowSparseMatrix::FromTripletSparseMatrix( +std::unique_ptr<CompressedRowSparseMatrix> +CompressedRowSparseMatrix::FromTripletSparseMatrix( const TripletSparseMatrix& input, bool transpose) { int num_rows = input.num_rows(); int num_cols = input.num_cols(); @@ -214,8 +217,9 @@ CompressedRowSparseMatrix* CompressedRowSparseMatrix::FromTripletSparseMatrix( input.num_nonzeros() * sizeof(int) + // NOLINT input.num_nonzeros() * sizeof(double)); // NOLINT - CompressedRowSparseMatrix* output = - new CompressedRowSparseMatrix(num_rows, num_cols, input.num_nonzeros()); + std::unique_ptr<CompressedRowSparseMatrix> output = + std::make_unique<CompressedRowSparseMatrix>( + num_rows, num_cols, input.num_nonzeros()); if (num_rows == 0) { // No data to copy. @@ -266,7 +270,7 @@ CompressedRowSparseMatrix::CompressedRowSparseMatrix(const double* diagonal, CHECK_EQ(num_nonzeros(), num_rows); } -CompressedRowSparseMatrix::~CompressedRowSparseMatrix() {} +CompressedRowSparseMatrix::~CompressedRowSparseMatrix() = default; void CompressedRowSparseMatrix::SetZero() { std::fill(values_.begin(), values_.end(), 0); @@ -533,17 +537,19 @@ void CompressedRowSparseMatrix::SetMaxNumNonZeros(int num_nonzeros) { values_.resize(num_nonzeros); } -CompressedRowSparseMatrix* CompressedRowSparseMatrix::CreateBlockDiagonalMatrix( +std::unique_ptr<CompressedRowSparseMatrix> +CompressedRowSparseMatrix::CreateBlockDiagonalMatrix( const double* diagonal, const vector<int>& blocks) { int num_rows = 0; int num_nonzeros = 0; - for (int i = 0; i < blocks.size(); ++i) { - num_rows += blocks[i]; - num_nonzeros += blocks[i] * blocks[i]; + for (int block_size : blocks) { + num_rows += block_size; + num_nonzeros += block_size * block_size; } - CompressedRowSparseMatrix* matrix = - new CompressedRowSparseMatrix(num_rows, num_rows, num_nonzeros); + std::unique_ptr<CompressedRowSparseMatrix> matrix = + std::make_unique<CompressedRowSparseMatrix>( + num_rows, num_rows, num_nonzeros); int* rows = matrix->mutable_rows(); int* cols = matrix->mutable_cols(); @@ -552,8 +558,7 @@ CompressedRowSparseMatrix* CompressedRowSparseMatrix::CreateBlockDiagonalMatrix( int idx_cursor = 0; int col_cursor = 0; - for (int i = 0; i < blocks.size(); ++i) { - const int block_size = blocks[i]; + for (int block_size : blocks) { for (int r = 0; r < block_size; ++r) { *(rows++) = idx_cursor; values[idx_cursor + r] = diagonal[col_cursor + r]; @@ -573,9 +578,11 @@ CompressedRowSparseMatrix* CompressedRowSparseMatrix::CreateBlockDiagonalMatrix( return matrix; } -CompressedRowSparseMatrix* CompressedRowSparseMatrix::Transpose() const { - CompressedRowSparseMatrix* transpose = - new CompressedRowSparseMatrix(num_cols_, num_rows_, num_nonzeros()); +std::unique_ptr<CompressedRowSparseMatrix> +CompressedRowSparseMatrix::Transpose() const { + std::unique_ptr<CompressedRowSparseMatrix> transpose = + std::make_unique<CompressedRowSparseMatrix>( + num_cols_, num_rows_, num_nonzeros()); switch (storage_type_) { case UNSYMMETRIC: @@ -612,7 +619,8 @@ CompressedRowSparseMatrix* CompressedRowSparseMatrix::Transpose() const { return transpose; } -CompressedRowSparseMatrix* CompressedRowSparseMatrix::CreateRandomMatrix( +std::unique_ptr<CompressedRowSparseMatrix> +CompressedRowSparseMatrix::CreateRandomMatrix( CompressedRowSparseMatrix::RandomMatrixOptions options) { CHECK_GT(options.num_row_blocks, 0); CHECK_GT(options.min_row_block_size, 0); @@ -714,7 +722,7 @@ CompressedRowSparseMatrix* CompressedRowSparseMatrix::CreateRandomMatrix( const int num_rows = std::accumulate(row_blocks.begin(), row_blocks.end(), 0); const int num_cols = std::accumulate(col_blocks.begin(), col_blocks.end(), 0); const bool kDoNotTranspose = false; - CompressedRowSparseMatrix* matrix = + std::unique_ptr<CompressedRowSparseMatrix> matrix = CompressedRowSparseMatrix::FromTripletSparseMatrix( TripletSparseMatrix( num_rows, num_cols, tsm_rows, tsm_cols, tsm_values), diff --git a/extern/ceres/internal/ceres/compressed_row_sparse_matrix.h b/extern/ceres/internal/ceres/compressed_row_sparse_matrix.h index 0a1b945193d..3d7d385b185 100644 --- a/extern/ceres/internal/ceres/compressed_row_sparse_matrix.h +++ b/extern/ceres/internal/ceres/compressed_row_sparse_matrix.h @@ -31,9 +31,11 @@ #ifndef CERES_INTERNAL_COMPRESSED_ROW_SPARSE_MATRIX_H_ #define CERES_INTERNAL_COMPRESSED_ROW_SPARSE_MATRIX_H_ +#include <memory> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/sparse_matrix.h" #include "ceres/types.h" #include "glog/logging.h" @@ -46,7 +48,7 @@ namespace internal { class TripletSparseMatrix; -class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { +class CERES_NO_EXPORT CompressedRowSparseMatrix : public SparseMatrix { public: enum StorageType { UNSYMMETRIC, @@ -63,9 +65,7 @@ class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { // entries. // // The storage type of the matrix is set to UNSYMMETRIC. - // - // Caller owns the result. - static CompressedRowSparseMatrix* FromTripletSparseMatrix( + static std::unique_ptr<CompressedRowSparseMatrix> FromTripletSparseMatrix( const TripletSparseMatrix& input); // Create a matrix with the same content as the TripletSparseMatrix @@ -73,10 +73,8 @@ class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { // entries. // // The storage type of the matrix is set to UNSYMMETRIC. - // - // Caller owns the result. - static CompressedRowSparseMatrix* FromTripletSparseMatrixTransposed( - const TripletSparseMatrix& input); + static std::unique_ptr<CompressedRowSparseMatrix> + FromTripletSparseMatrixTransposed(const TripletSparseMatrix& input); // Use this constructor only if you know what you are doing. This // creates a "blank" matrix with the appropriate amount of memory @@ -100,7 +98,7 @@ class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { CompressedRowSparseMatrix(const double* diagonal, int num_rows); // SparseMatrix interface. - virtual ~CompressedRowSparseMatrix(); + ~CompressedRowSparseMatrix() override; void SetZero() final; void RightMultiply(const double* x, double* y) const final; void LeftMultiply(const double* x, double* y) const final; @@ -124,7 +122,7 @@ class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { void ToCRSMatrix(CRSMatrix* matrix) const; - CompressedRowSparseMatrix* Transpose() const; + std::unique_ptr<CompressedRowSparseMatrix> Transpose() const; // Destructive array resizing method. void SetMaxNumNonZeros(int num_nonzeros); @@ -140,7 +138,7 @@ class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { const int* rows() const { return &rows_[0]; } int* mutable_rows() { return &rows_[0]; } - const StorageType storage_type() const { return storage_type_; } + StorageType storage_type() const { return storage_type_; } void set_storage_type(const StorageType storage_type) { storage_type_ = storage_type; } @@ -154,9 +152,7 @@ class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { // Create a block diagonal CompressedRowSparseMatrix with the given // block structure. The individual blocks are assumed to be laid out // contiguously in the diagonal array, one block at a time. - // - // Caller owns the result. - static CompressedRowSparseMatrix* CreateBlockDiagonalMatrix( + static std::unique_ptr<CompressedRowSparseMatrix> CreateBlockDiagonalMatrix( const double* diagonal, const std::vector<int>& blocks); // Options struct to control the generation of random block sparse @@ -198,13 +194,11 @@ class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { // Create a random CompressedRowSparseMatrix whose entries are // normally distributed and whose structure is determined by // RandomMatrixOptions. - // - // Caller owns the result. - static CompressedRowSparseMatrix* CreateRandomMatrix( + static std::unique_ptr<CompressedRowSparseMatrix> CreateRandomMatrix( RandomMatrixOptions options); private: - static CompressedRowSparseMatrix* FromTripletSparseMatrix( + static std::unique_ptr<CompressedRowSparseMatrix> FromTripletSparseMatrix( const TripletSparseMatrix& input, bool transpose); int num_rows_; @@ -226,4 +220,6 @@ class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_COMPRESSED_ROW_SPARSE_MATRIX_H_ diff --git a/extern/ceres/internal/ceres/concurrent_queue.h b/extern/ceres/internal/ceres/concurrent_queue.h index a04d147c5c0..1e74153566a 100644 --- a/extern/ceres/internal/ceres/concurrent_queue.h +++ b/extern/ceres/internal/ceres/concurrent_queue.h @@ -78,7 +78,7 @@ template <typename T> class ConcurrentQueue { public: // Defaults the queue to blocking on Wait calls. - ConcurrentQueue() : wait_(true) {} + ConcurrentQueue() = default; // Atomically push an element onto the queue. If a thread was waiting for an // element, wake it up. @@ -149,7 +149,7 @@ class ConcurrentQueue { std::queue<T> queue_; // If true, signals that callers of Wait will block waiting to pop an // element off the queue. - bool wait_; + bool wait_{true}; }; } // namespace internal diff --git a/extern/ceres/internal/ceres/conditioned_cost_function.cc b/extern/ceres/internal/ceres/conditioned_cost_function.cc index fb4c52af084..a9013a23d0a 100644 --- a/extern/ceres/internal/ceres/conditioned_cost_function.cc +++ b/extern/ceres/internal/ceres/conditioned_cost_function.cc @@ -98,7 +98,7 @@ bool ConditionedCostFunction::Evaluate(double const* const* parameters, double** conditioner_derivative_pointer2 = &conditioner_derivative_pointer; if (!jacobians) { - conditioner_derivative_pointer2 = NULL; + conditioner_derivative_pointer2 = nullptr; } double unconditioned_residual = residuals[r]; diff --git a/extern/ceres/internal/ceres/conjugate_gradients_solver.cc b/extern/ceres/internal/ceres/conjugate_gradients_solver.cc index 3019628a16c..62ae9201cb5 100644 --- a/extern/ceres/internal/ceres/conjugate_gradients_solver.cc +++ b/extern/ceres/internal/ceres/conjugate_gradients_solver.cc @@ -41,6 +41,7 @@ #include <cmath> #include <cstddef> +#include <utility> #include "ceres/internal/eigen.h" #include "ceres/linear_operator.h" @@ -57,8 +58,8 @@ bool IsZeroOrInfinity(double x) { return ((x == 0.0) || std::isinf(x)); } } // namespace ConjugateGradientsSolver::ConjugateGradientsSolver( - const LinearSolver::Options& options) - : options_(options) {} + LinearSolver::Options options) + : options_(std::move(options)) {} LinearSolver::Summary ConjugateGradientsSolver::Solve( LinearOperator* A, @@ -112,7 +113,7 @@ LinearSolver::Summary ConjugateGradientsSolver::Solve( for (summary.num_iterations = 1;; ++summary.num_iterations) { // Apply preconditioner - if (per_solve_options.preconditioner != NULL) { + if (per_solve_options.preconditioner != nullptr) { z.setZero(); per_solve_options.preconditioner->RightMultiply(r.data(), z.data()); } else { diff --git a/extern/ceres/internal/ceres/conjugate_gradients_solver.h b/extern/ceres/internal/ceres/conjugate_gradients_solver.h index f79ca496531..99ddb5d485b 100644 --- a/extern/ceres/internal/ceres/conjugate_gradients_solver.h +++ b/extern/ceres/internal/ceres/conjugate_gradients_solver.h @@ -34,7 +34,8 @@ #ifndef CERES_INTERNAL_CONJUGATE_GRADIENTS_SOLVER_H_ #define CERES_INTERNAL_CONJUGATE_GRADIENTS_SOLVER_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { @@ -55,9 +56,9 @@ class LinearOperator; // For more details see the documentation for // LinearSolver::PerSolveOptions::r_tolerance and // LinearSolver::PerSolveOptions::q_tolerance in linear_solver.h. -class CERES_EXPORT_INTERNAL ConjugateGradientsSolver : public LinearSolver { +class CERES_NO_EXPORT ConjugateGradientsSolver final : public LinearSolver { public: - explicit ConjugateGradientsSolver(const LinearSolver::Options& options); + explicit ConjugateGradientsSolver(LinearSolver::Options options); Summary Solve(LinearOperator* A, const double* b, const LinearSolver::PerSolveOptions& per_solve_options, @@ -70,4 +71,6 @@ class CERES_EXPORT_INTERNAL ConjugateGradientsSolver : public LinearSolver { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_CONJUGATE_GRADIENTS_SOLVER_H_ diff --git a/extern/ceres/internal/ceres/context.cc b/extern/ceres/internal/ceres/context.cc index 55e76351219..fde16b84bca 100644 --- a/extern/ceres/internal/ceres/context.cc +++ b/extern/ceres/internal/ceres/context.cc @@ -34,6 +34,8 @@ namespace ceres { +Context::Context() = default; Context* Context::Create() { return new internal::ContextImpl(); } +Context::~Context() = default; } // namespace ceres diff --git a/extern/ceres/internal/ceres/context_impl.cc b/extern/ceres/internal/ceres/context_impl.cc index 20fe5cbab2a..a4b3c842da1 100644 --- a/extern/ceres/internal/ceres/context_impl.cc +++ b/extern/ceres/internal/ceres/context_impl.cc @@ -30,9 +30,75 @@ #include "ceres/context_impl.h" +#include <string> + +#include "ceres/internal/config.h" + +#ifndef CERES_NO_CUDA +#include "cublas_v2.h" +#include "cuda_runtime.h" +#include "cusolverDn.h" +#endif // CERES_NO_CUDA + namespace ceres { namespace internal { +ContextImpl::ContextImpl() = default; + +#ifndef CERES_NO_CUDA +bool ContextImpl::InitCUDA(std::string* message) { + if (cuda_initialized_) { + return true; + } + if (cublasCreate(&cublas_handle_) != CUBLAS_STATUS_SUCCESS) { + *message = "cuBLAS::cublasCreate failed."; + cublas_handle_ = nullptr; + return false; + } + if (cusolverDnCreate(&cusolver_handle_) != CUSOLVER_STATUS_SUCCESS) { + *message = "cuSolverDN::cusolverDnCreate failed."; + cusolver_handle_ = nullptr; + cublasDestroy(cublas_handle_); + cublas_handle_ = nullptr; + return false; + } + if (cudaStreamCreateWithFlags(&stream_, cudaStreamNonBlocking) != + cudaSuccess) { + *message = "CUDA::cudaStreamCreateWithFlags failed."; + cusolverDnDestroy(cusolver_handle_); + cublasDestroy(cublas_handle_); + cusolver_handle_ = nullptr; + cublas_handle_ = nullptr; + stream_ = nullptr; + return false; + } + if (cusolverDnSetStream(cusolver_handle_, stream_) != + CUSOLVER_STATUS_SUCCESS || + cublasSetStream(cublas_handle_, stream_) != CUBLAS_STATUS_SUCCESS) { + *message = + "cuSolverDN::cusolverDnSetStream or cuBLAS::cublasSetStream failed."; + cusolverDnDestroy(cusolver_handle_); + cublasDestroy(cublas_handle_); + cudaStreamDestroy(stream_); + cusolver_handle_ = nullptr; + cublas_handle_ = nullptr; + stream_ = nullptr; + return false; + } + cuda_initialized_ = true; + return true; +} +#endif // CERES_NO_CUDA + +ContextImpl::~ContextImpl() { +#ifndef CERES_NO_CUDA + if (cuda_initialized_) { + cusolverDnDestroy(cusolver_handle_); + cublasDestroy(cublas_handle_); + cudaStreamDestroy(stream_); + } +#endif // CERES_NO_CUDA +} void ContextImpl::EnsureMinimumThreads(int num_threads) { #ifdef CERES_USE_CXX_THREADS thread_pool.Resize(num_threads); diff --git a/extern/ceres/internal/ceres/context_impl.h b/extern/ceres/internal/ceres/context_impl.h index 574d1efcc6d..8e9a03fb4ae 100644 --- a/extern/ceres/internal/ceres/context_impl.h +++ b/extern/ceres/internal/ceres/context_impl.h @@ -33,10 +33,20 @@ // This include must come before any #ifndef check on Ceres compile options. // clang-format off -#include "ceres/internal/port.h" -// clanf-format on +#include "ceres/internal/config.h" +// clang-format on + +#include <string> #include "ceres/context.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" + +#ifndef CERES_NO_CUDA +#include "cublas_v2.h" +#include "cuda_runtime.h" +#include "cusolverDn.h" +#endif // CERES_NO_CUDA #ifdef CERES_USE_CXX_THREADS #include "ceres/thread_pool.h" @@ -45,14 +55,13 @@ namespace ceres { namespace internal { -class CERES_EXPORT_INTERNAL ContextImpl : public Context { +class CERES_NO_EXPORT ContextImpl final : public Context { public: - ContextImpl() {} + ContextImpl(); + ~ContextImpl() override; ContextImpl(const ContextImpl&) = delete; void operator=(const ContextImpl&) = delete; - virtual ~ContextImpl() {} - // When compiled with C++ threading support, resize the thread pool to have // at min(num_thread, num_hardware_threads) where num_hardware_threads is // defined by the hardware. Otherwise this call is a no-op. @@ -61,9 +70,28 @@ class CERES_EXPORT_INTERNAL ContextImpl : public Context { #ifdef CERES_USE_CXX_THREADS ThreadPool thread_pool; #endif // CERES_USE_CXX_THREADS + +#ifndef CERES_NO_CUDA + // Initializes the cuSolverDN context, creates an asynchronous stream, and + // associates the stream with cuSolverDN. Returns true iff initialization was + // successful, else it returns false and a human-readable error message is + // returned. + bool InitCUDA(std::string* message); + + // Handle to the cuSOLVER context. + cusolverDnHandle_t cusolver_handle_ = nullptr; + // Handle to cuBLAS context. + cublasHandle_t cublas_handle_ = nullptr; + // CUDA device stream. + cudaStream_t stream_ = nullptr; + // Indicates whether all the CUDA resources have been initialized. + bool cuda_initialized_ = false; +#endif // CERES_NO_CUDA }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_CONTEXT_IMPL_H_ diff --git a/extern/ceres/internal/ceres/coordinate_descent_minimizer.cc b/extern/ceres/internal/ceres/coordinate_descent_minimizer.cc index 93096ac0728..a6e149d1cee 100644 --- a/extern/ceres/internal/ceres/coordinate_descent_minimizer.cc +++ b/extern/ceres/internal/ceres/coordinate_descent_minimizer.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -64,7 +64,7 @@ CoordinateDescentMinimizer::CoordinateDescentMinimizer(ContextImpl* context) CHECK(context_ != nullptr); } -CoordinateDescentMinimizer::~CoordinateDescentMinimizer() {} +CoordinateDescentMinimizer::~CoordinateDescentMinimizer() = default; bool CoordinateDescentMinimizer::Init( const Program& program, @@ -94,9 +94,9 @@ bool CoordinateDescentMinimizer::Init( // assign zero offsets/empty independent sets to these parameter // blocks. const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks(); - for (int i = 0; i < parameter_blocks.size(); ++i) { - if (!ordering.IsMember(parameter_blocks[i]->mutable_user_state())) { - parameter_blocks_.push_back(parameter_blocks[i]); + for (auto* parameter_block : parameter_blocks) { + if (!ordering.IsMember(parameter_block->mutable_user_state())) { + parameter_blocks_.push_back(parameter_block); independent_set_offsets_.push_back(independent_set_offsets_.back()); } } @@ -105,8 +105,7 @@ bool CoordinateDescentMinimizer::Init( // block. residual_blocks_.resize(parameter_block_index.size()); const vector<ResidualBlock*>& residual_blocks = program.residual_blocks(); - for (int i = 0; i < residual_blocks.size(); ++i) { - ResidualBlock* residual_block = residual_blocks[i]; + for (auto* residual_block : residual_blocks) { const int num_parameter_blocks = residual_block->NumParameterBlocks(); for (int j = 0; j < num_parameter_blocks; ++j) { ParameterBlock* parameter_block = residual_block->parameter_blocks()[j]; @@ -129,14 +128,15 @@ void CoordinateDescentMinimizer::Minimize(const Minimizer::Options& options, double* parameters, Solver::Summary* summary) { // Set the state and mark all parameter blocks constant. - for (int i = 0; i < parameter_blocks_.size(); ++i) { - ParameterBlock* parameter_block = parameter_blocks_[i]; + for (auto* parameter_block : parameter_blocks_) { parameter_block->SetState(parameters + parameter_block->state_offset()); parameter_block->SetConstant(); } - std::unique_ptr<LinearSolver*[]> linear_solvers( - new LinearSolver*[options.num_threads]); + std::vector<std::unique_ptr<LinearSolver>> linear_solvers( + options.num_threads); + // std::unique_ptr<LinearSolver*[]> linear_solvers( + // new LinearSolver*[options.num_threads]); LinearSolver::Options linear_solver_options; linear_solver_options.type = DENSE_QR; @@ -188,7 +188,7 @@ void CoordinateDescentMinimizer::Minimize(const Minimizer::Options& options, // we are fine. Solver::Summary inner_summary; Solve(&inner_program, - linear_solvers[thread_id], + linear_solvers[thread_id].get(), parameters + parameter_block->state_offset(), &inner_summary); @@ -200,13 +200,13 @@ void CoordinateDescentMinimizer::Minimize(const Minimizer::Options& options, }); } - for (int i = 0; i < parameter_blocks_.size(); ++i) { - parameter_blocks_[i]->SetVarying(); + for (auto* parameter_block : parameter_blocks_) { + parameter_block->SetVarying(); } - for (int i = 0; i < options.num_threads; ++i) { - delete linear_solvers[i]; - } + // for (int i = 0; i < options.num_threads; ++i) { + // delete linear_solvers[i]; + //} } // Solve the optimization problem for one parameter block. @@ -221,17 +221,16 @@ void CoordinateDescentMinimizer::Solve(Program* program, string error; Minimizer::Options minimizer_options; - minimizer_options.evaluator.reset( - Evaluator::Create(evaluator_options_, program, &error)); + minimizer_options.evaluator = + Evaluator::Create(evaluator_options_, program, &error); CHECK(minimizer_options.evaluator != nullptr); - minimizer_options.jacobian.reset( - minimizer_options.evaluator->CreateJacobian()); + minimizer_options.jacobian = minimizer_options.evaluator->CreateJacobian(); CHECK(minimizer_options.jacobian != nullptr); TrustRegionStrategy::Options trs_options; trs_options.linear_solver = linear_solver; - minimizer_options.trust_region_strategy.reset( - TrustRegionStrategy::Create(trs_options)); + minimizer_options.trust_region_strategy = + TrustRegionStrategy::Create(trs_options); CHECK(minimizer_options.trust_region_strategy != nullptr); minimizer_options.is_silent = true; @@ -263,12 +262,12 @@ bool CoordinateDescentMinimizer::IsOrderingValid( // of independent sets of decreasing size and invert it. This // seems to work better in practice, i.e., Cameras before // points. -ParameterBlockOrdering* CoordinateDescentMinimizer::CreateOrdering( - const Program& program) { - std::unique_ptr<ParameterBlockOrdering> ordering(new ParameterBlockOrdering); +std::shared_ptr<ParameterBlockOrdering> +CoordinateDescentMinimizer::CreateOrdering(const Program& program) { + auto ordering = std::make_shared<ParameterBlockOrdering>(); ComputeRecursiveIndependentSetOrdering(program, ordering.get()); ordering->Reverse(); - return ordering.release(); + return ordering; } } // namespace internal diff --git a/extern/ceres/internal/ceres/coordinate_descent_minimizer.h b/extern/ceres/internal/ceres/coordinate_descent_minimizer.h index 7d17d53eb0f..75f26480c88 100644 --- a/extern/ceres/internal/ceres/coordinate_descent_minimizer.h +++ b/extern/ceres/internal/ceres/coordinate_descent_minimizer.h @@ -56,7 +56,7 @@ class LinearSolver; // // The minimizer assumes that none of the parameter blocks in the // program are constant. -class CoordinateDescentMinimizer : public Minimizer { +class CERES_NO_EXPORT CoordinateDescentMinimizer final : public Minimizer { public: explicit CoordinateDescentMinimizer(ContextImpl* context); @@ -66,7 +66,7 @@ class CoordinateDescentMinimizer : public Minimizer { std::string* error); // Minimizer interface. - virtual ~CoordinateDescentMinimizer(); + ~CoordinateDescentMinimizer() override; void Minimize(const Minimizer::Options& options, double* parameters, @@ -81,7 +81,8 @@ class CoordinateDescentMinimizer : public Minimizer { // of independent sets of decreasing size and invert it. This // seems to work better in practice, i.e., Cameras before // points. - static ParameterBlockOrdering* CreateOrdering(const Program& program); + static std::shared_ptr<ParameterBlockOrdering> CreateOrdering( + const Program& program); private: void Solve(Program* program, diff --git a/extern/ceres/internal/ceres/corrector.cc b/extern/ceres/internal/ceres/corrector.cc index 6a79a06a544..bf3ba9c5714 100644 --- a/extern/ceres/internal/ceres/corrector.cc +++ b/extern/ceres/internal/ceres/corrector.cc @@ -111,7 +111,7 @@ Corrector::Corrector(const double sq_norm, const double rho[3]) { } void Corrector::CorrectResiduals(const int num_rows, double* residuals) { - DCHECK(residuals != NULL); + DCHECK(residuals != nullptr); // Equation 11 in BANS. VectorRef(residuals, num_rows) *= residual_scaling_; } @@ -120,8 +120,8 @@ void Corrector::CorrectJacobian(const int num_rows, const int num_cols, double* residuals, double* jacobian) { - DCHECK(residuals != NULL); - DCHECK(jacobian != NULL); + DCHECK(residuals != nullptr); + DCHECK(jacobian != nullptr); // The common case (rho[2] <= 0). if (alpha_sq_norm_ == 0.0) { diff --git a/extern/ceres/internal/ceres/corrector.h b/extern/ceres/internal/ceres/corrector.h index 3e11cdce1ae..44379a3ea7a 100644 --- a/extern/ceres/internal/ceres/corrector.h +++ b/extern/ceres/internal/ceres/corrector.h @@ -35,7 +35,8 @@ #ifndef CERES_INTERNAL_CORRECTOR_H_ #define CERES_INTERNAL_CORRECTOR_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -48,7 +49,7 @@ namespace internal { // gauss newton approximation and then take its square root to get the // corresponding corrections to the residual and jacobian. For the // full expressions see Eq. 10 and 11 in BANS by Triggs et al. -class CERES_EXPORT_INTERNAL Corrector { +class CERES_NO_EXPORT Corrector { public: // The constructor takes the squared norm, the value, the first and // second derivatives of the LossFunction. It precalculates some of @@ -89,4 +90,6 @@ class CERES_EXPORT_INTERNAL Corrector { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_CORRECTOR_H_ diff --git a/extern/ceres/internal/ceres/blas.h b/extern/ceres/internal/ceres/cost_function.cc index a43301c5d18..7597b431ec9 100644 --- a/extern/ceres/internal/ceres/blas.h +++ b/extern/ceres/internal/ceres/cost_function.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -27,31 +27,13 @@ // POSSIBILITY OF SUCH DAMAGE. // // Author: sameeragarwal@google.com (Sameer Agarwal) -// -// Wrapper functions around BLAS functions. +// keir@google.m (Keir Mierle) -#ifndef CERES_INTERNAL_BLAS_H_ -#define CERES_INTERNAL_BLAS_H_ +#include "ceres/cost_function.h" namespace ceres { -namespace internal { -class BLAS { - public: - // transpose = true : c = alpha * a'a + beta * c; - // transpose = false : c = alpha * aa' + beta * c; - // - // Assumes column major matrices. - static void SymmetricRankKUpdate(int num_rows, - int num_cols, - const double* a, - bool transpose, - double alpha, - double beta, - double* c); -}; +CostFunction::CostFunction() : num_residuals_(0) {} +CostFunction::~CostFunction() = default; -} // namespace internal } // namespace ceres - -#endif // CERES_INTERNAL_BLAS_H_ diff --git a/extern/ceres/internal/ceres/covariance.cc b/extern/ceres/internal/ceres/covariance.cc index 8e240ff317c..d63dd3789c3 100644 --- a/extern/ceres/internal/ceres/covariance.cc +++ b/extern/ceres/internal/ceres/covariance.cc @@ -39,15 +39,14 @@ namespace ceres { -using std::make_pair; using std::pair; using std::vector; Covariance::Covariance(const Covariance::Options& options) { - impl_.reset(new internal::CovarianceImpl(options)); + impl_ = std::make_unique<internal::CovarianceImpl>(options); } -Covariance::~Covariance() {} +Covariance::~Covariance() = default; bool Covariance::Compute( const vector<pair<const double*, const double*>>& covariance_blocks, diff --git a/extern/ceres/internal/ceres/covariance_impl.cc b/extern/ceres/internal/ceres/covariance_impl.cc index 1f86707f5a7..324b5531a04 100644 --- a/extern/ceres/internal/ceres/covariance_impl.cc +++ b/extern/ceres/internal/ceres/covariance_impl.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -79,13 +79,12 @@ CovarianceImpl::CovarianceImpl(const Covariance::Options& options) evaluate_options_.apply_loss_function = options_.apply_loss_function; } -CovarianceImpl::~CovarianceImpl() {} +CovarianceImpl::~CovarianceImpl() = default; template <typename T> void CheckForDuplicates(std::vector<T> blocks) { - sort(blocks.begin(), blocks.end()); - typename std::vector<T>::iterator it = - std::adjacent_find(blocks.begin(), blocks.end()); + std::sort(blocks.begin(), blocks.end()); + auto it = std::adjacent_find(blocks.begin(), blocks.end()); if (it != blocks.end()) { // In case there are duplicates, we search for their location. std::map<T, std::vector<int>> blocks_map; @@ -117,7 +116,7 @@ bool CovarianceImpl::Compute(const CovarianceBlocks& covariance_blocks, covariance_blocks); problem_ = problem; parameter_block_to_row_index_.clear(); - covariance_matrix_.reset(NULL); + covariance_matrix_ = nullptr; is_valid_ = (ComputeCovarianceSparsity(covariance_blocks, problem) && ComputeCovarianceValues()); is_computed_ = true; @@ -162,10 +161,10 @@ bool CovarianceImpl::GetCovarianceBlockInTangentOrAmbientSpace( const int block1_size = block1->Size(); const int block2_size = block2->Size(); - const int block1_local_size = block1->LocalSize(); - const int block2_local_size = block2->LocalSize(); + const int block1_tangent_size = block1->TangentSize(); + const int block2_tangent_size = block2->TangentSize(); if (!lift_covariance_to_ambient_space) { - MatrixRef(covariance_block, block1_local_size, block2_local_size) + MatrixRef(covariance_block, block1_tangent_size, block2_tangent_size) .setZero(); } else { MatrixRef(covariance_block, block1_size, block2_size).setZero(); @@ -209,34 +208,34 @@ bool CovarianceImpl::GetCovarianceBlockInTangentOrAmbientSpace( FindOrDie(parameter_map, const_cast<double*>(parameter_block1)); ParameterBlock* block2 = FindOrDie(parameter_map, const_cast<double*>(parameter_block2)); - const LocalParameterization* local_param1 = block1->local_parameterization(); - const LocalParameterization* local_param2 = block2->local_parameterization(); + const Manifold* manifold1 = block1->manifold(); + const Manifold* manifold2 = block2->manifold(); const int block1_size = block1->Size(); - const int block1_local_size = block1->LocalSize(); + const int block1_tangent_size = block1->TangentSize(); const int block2_size = block2->Size(); - const int block2_local_size = block2->LocalSize(); + const int block2_tangent_size = block2->TangentSize(); - ConstMatrixRef cov( - covariance_matrix_->values() + rows[row_begin], block1_size, row_size); + ConstMatrixRef cov(covariance_matrix_->values() + rows[row_begin], + block1_tangent_size, + row_size); - // Fast path when there are no local parameterizations or if the - // user does not want it lifted to the ambient space. - if ((local_param1 == NULL && local_param2 == NULL) || + // Fast path when there are no manifolds or if the user does not want it + // lifted to the ambient space. + if ((manifold1 == nullptr && manifold2 == nullptr) || !lift_covariance_to_ambient_space) { if (transpose) { - MatrixRef(covariance_block, block2_local_size, block1_local_size) = - cov.block(0, offset, block1_local_size, block2_local_size) + MatrixRef(covariance_block, block2_tangent_size, block1_tangent_size) = + cov.block(0, offset, block1_tangent_size, block2_tangent_size) .transpose(); } else { - MatrixRef(covariance_block, block1_local_size, block2_local_size) = - cov.block(0, offset, block1_local_size, block2_local_size); + MatrixRef(covariance_block, block1_tangent_size, block2_tangent_size) = + cov.block(0, offset, block1_tangent_size, block2_tangent_size); } return true; } - // If local parameterizations are used then the covariance that has - // been computed is in the tangent space and it needs to be lifted - // back to the ambient space. + // If manifolds are used then the covariance that has been computed is in the + // tangent space and it needs to be lifted back to the ambient space. // // This is given by the formula // @@ -249,36 +248,37 @@ bool CovarianceImpl::GetCovarianceBlockInTangentOrAmbientSpace( // See Result 5.11 on page 142 of Hartley & Zisserman (2nd Edition) // for a proof. // - // TODO(sameeragarwal): Add caching of local parameterization, so - // that they are computed just once per parameter block. - Matrix block1_jacobian(block1_size, block1_local_size); - if (local_param1 == NULL) { + // TODO(sameeragarwal): Add caching the manifold plus_jacobian, so that they + // are computed just once per parameter block. + Matrix block1_jacobian(block1_size, block1_tangent_size); + if (manifold1 == nullptr) { block1_jacobian.setIdentity(); } else { - local_param1->ComputeJacobian(parameter_block1, block1_jacobian.data()); + manifold1->PlusJacobian(parameter_block1, block1_jacobian.data()); } - Matrix block2_jacobian(block2_size, block2_local_size); + Matrix block2_jacobian(block2_size, block2_tangent_size); // Fast path if the user is requesting a diagonal block. if (parameter_block1 == parameter_block2) { block2_jacobian = block1_jacobian; } else { - if (local_param2 == NULL) { + if (manifold2 == nullptr) { block2_jacobian.setIdentity(); } else { - local_param2->ComputeJacobian(parameter_block2, block2_jacobian.data()); + manifold2->PlusJacobian(parameter_block2, block2_jacobian.data()); } } if (transpose) { MatrixRef(covariance_block, block2_size, block1_size) = block2_jacobian * - cov.block(0, offset, block1_local_size, block2_local_size).transpose() * + cov.block(0, offset, block1_tangent_size, block2_tangent_size) + .transpose() * block1_jacobian.transpose(); } else { MatrixRef(covariance_block, block1_size, block2_size) = block1_jacobian * - cov.block(0, offset, block1_local_size, block2_local_size) * + cov.block(0, offset, block1_tangent_size, block2_tangent_size) * block2_jacobian.transpose(); } @@ -309,7 +309,7 @@ bool CovarianceImpl::GetCovarianceMatrixInTangentOrAmbientSpace( if (lift_covariance_to_ambient_space) { parameter_sizes.push_back(block->Size()); } else { - parameter_sizes.push_back(block->LocalSize()); + parameter_sizes.push_back(block->TangentSize()); } } std::partial_sum(parameter_sizes.begin(), @@ -383,8 +383,7 @@ bool CovarianceImpl::ComputeCovarianceSparsity( std::vector<ResidualBlock*> residual_blocks; problem->GetResidualBlocks(&residual_blocks); - for (int i = 0; i < residual_blocks.size(); ++i) { - ResidualBlock* residual_block = residual_blocks[i]; + for (auto* residual_block : residual_blocks) { parameter_blocks_in_use.insert(residual_block->parameter_blocks(), residual_block->parameter_blocks() + residual_block->NumParameterBlocks()); @@ -394,8 +393,7 @@ bool CovarianceImpl::ComputeCovarianceSparsity( std::vector<double*>& active_parameter_blocks = evaluate_options_.parameter_blocks; active_parameter_blocks.clear(); - for (int i = 0; i < all_parameter_blocks.size(); ++i) { - double* parameter_block = all_parameter_blocks[i]; + for (auto* parameter_block : all_parameter_blocks) { ParameterBlock* block = FindOrDie(parameter_map, parameter_block); if (!block->IsConstant() && (parameter_blocks_in_use.count(block) > 0)) { active_parameter_blocks.push_back(parameter_block); @@ -411,10 +409,9 @@ bool CovarianceImpl::ComputeCovarianceSparsity( // ordering of parameter blocks just constructed. int num_rows = 0; parameter_block_to_row_index_.clear(); - for (int i = 0; i < active_parameter_blocks.size(); ++i) { - double* parameter_block = active_parameter_blocks[i]; + for (auto* parameter_block : active_parameter_blocks) { const int parameter_block_size = - problem->ParameterBlockLocalSize(parameter_block); + problem->ParameterBlockTangentSize(parameter_block); parameter_block_to_row_index_[parameter_block] = num_rows; num_rows += parameter_block_size; } @@ -424,9 +421,7 @@ bool CovarianceImpl::ComputeCovarianceSparsity( // triangular part of the matrix. int num_nonzeros = 0; CovarianceBlocks covariance_blocks; - for (int i = 0; i < original_covariance_blocks.size(); ++i) { - const std::pair<const double*, const double*>& block_pair = - original_covariance_blocks[i]; + for (const auto& block_pair : original_covariance_blocks) { if (constant_parameter_blocks_.count(block_pair.first) > 0 || constant_parameter_blocks_.count(block_pair.second) > 0) { continue; @@ -434,8 +429,8 @@ bool CovarianceImpl::ComputeCovarianceSparsity( int index1 = FindOrDie(parameter_block_to_row_index_, block_pair.first); int index2 = FindOrDie(parameter_block_to_row_index_, block_pair.second); - const int size1 = problem->ParameterBlockLocalSize(block_pair.first); - const int size2 = problem->ParameterBlockLocalSize(block_pair.second); + const int size1 = problem->ParameterBlockTangentSize(block_pair.first); + const int size2 = problem->ParameterBlockTangentSize(block_pair.second); num_nonzeros += size1 * size2; // Make sure we are constructing a block upper triangular matrix. @@ -447,9 +442,9 @@ bool CovarianceImpl::ComputeCovarianceSparsity( } } - if (covariance_blocks.size() == 0) { + if (covariance_blocks.empty()) { VLOG(2) << "No non-zero covariance blocks found"; - covariance_matrix_.reset(NULL); + covariance_matrix_ = nullptr; return true; } @@ -459,8 +454,8 @@ bool CovarianceImpl::ComputeCovarianceSparsity( std::sort(covariance_blocks.begin(), covariance_blocks.end()); // Fill the sparsity pattern of the covariance matrix. - covariance_matrix_.reset( - new CompressedRowSparseMatrix(num_rows, num_rows, num_nonzeros)); + covariance_matrix_ = std::make_unique<CompressedRowSparseMatrix>( + num_rows, num_rows, num_nonzeros); int* rows = covariance_matrix_->mutable_rows(); int* cols = covariance_matrix_->mutable_cols(); @@ -480,7 +475,7 @@ bool CovarianceImpl::ComputeCovarianceSparsity( int cursor = 0; // index into the covariance matrix. for (const auto& entry : parameter_block_to_row_index_) { const double* row_block = entry.first; - const int row_block_size = problem->ParameterBlockLocalSize(row_block); + const int row_block_size = problem->ParameterBlockTangentSize(row_block); int row_begin = entry.second; // Iterate over the covariance blocks contained in this row block @@ -493,7 +488,7 @@ bool CovarianceImpl::ComputeCovarianceSparsity( if (block_pair.first != row_block) { break; } - num_columns += problem->ParameterBlockLocalSize(block_pair.second); + num_columns += problem->ParameterBlockTangentSize(block_pair.second); } // Fill out all the compressed rows for this parameter block. @@ -501,7 +496,8 @@ bool CovarianceImpl::ComputeCovarianceSparsity( rows[row_begin + r] = cursor; for (int c = 0; c < num_col_blocks; ++c) { const double* col_block = covariance_blocks[i + c].second; - const int col_block_size = problem->ParameterBlockLocalSize(col_block); + const int col_block_size = + problem->ParameterBlockTangentSize(col_block); int col_begin = FindOrDie(parameter_block_to_row_index_, col_block); for (int k = 0; k < col_block_size; ++k) { cols[cursor++] = col_begin++; @@ -556,13 +552,13 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingSuiteSparseQR() { "CovarianceImpl::ComputeCovarianceValuesUsingSparseQR"); #ifndef CERES_NO_SUITESPARSE - if (covariance_matrix_.get() == NULL) { + if (covariance_matrix_ == nullptr) { // Nothing to do, all zeros covariance matrix. return true; } CRSMatrix jacobian; - problem_->Evaluate(evaluate_options_, NULL, NULL, NULL, &jacobian); + problem_->Evaluate(evaluate_options_, nullptr, nullptr, nullptr, &jacobian); event_logger.AddEvent("Evaluate"); // Construct a compressed column form of the Jacobian. @@ -601,11 +597,11 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingSuiteSparseQR() { cholmod_jacobian.nrow = num_rows; cholmod_jacobian.ncol = num_cols; cholmod_jacobian.nzmax = num_nonzeros; - cholmod_jacobian.nz = NULL; + cholmod_jacobian.nz = nullptr; cholmod_jacobian.p = reinterpret_cast<void*>(&transpose_rows[0]); cholmod_jacobian.i = reinterpret_cast<void*>(&transpose_cols[0]); cholmod_jacobian.x = reinterpret_cast<void*>(&transpose_values[0]); - cholmod_jacobian.z = NULL; + cholmod_jacobian.z = nullptr; cholmod_jacobian.stype = 0; // Matrix is not symmetric. cholmod_jacobian.itype = CHOLMOD_LONG; cholmod_jacobian.xtype = CHOLMOD_REAL; @@ -616,8 +612,8 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingSuiteSparseQR() { cholmod_common cc; cholmod_l_start(&cc); - cholmod_sparse* R = NULL; - SuiteSparse_long* permutation = NULL; + cholmod_sparse* R = nullptr; + SuiteSparse_long* permutation = nullptr; // Compute a Q-less QR factorization of the Jacobian. Since we are // only interested in inverting J'J = R'R, we do not need Q. This @@ -648,9 +644,9 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingSuiteSparseQR() { } if (rank < cholmod_jacobian.ncol) { - LOG(ERROR) << "Jacobian matrix is rank deficient. " - << "Number of columns: " << cholmod_jacobian.ncol - << " rank: " << rank; + LOG(WARNING) << "Jacobian matrix is rank deficient. " + << "Number of columns: " << cholmod_jacobian.ncol + << " rank: " << rank; free(permutation); cholmod_l_free_sparse(&R, &cc); cholmod_l_finish(&cc); @@ -721,13 +717,13 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingSuiteSparseQR() { bool CovarianceImpl::ComputeCovarianceValuesUsingDenseSVD() { EventLogger event_logger( "CovarianceImpl::ComputeCovarianceValuesUsingDenseSVD"); - if (covariance_matrix_.get() == NULL) { + if (covariance_matrix_ == nullptr) { // Nothing to do, all zeros covariance matrix. return true; } CRSMatrix jacobian; - problem_->Evaluate(evaluate_options_, NULL, NULL, NULL, &jacobian); + problem_->Evaluate(evaluate_options_, nullptr, nullptr, nullptr, &jacobian); event_logger.AddEvent("Evaluate"); Matrix dense_jacobian(jacobian.num_rows, jacobian.num_cols); @@ -812,20 +808,20 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingDenseSVD() { bool CovarianceImpl::ComputeCovarianceValuesUsingEigenSparseQR() { EventLogger event_logger( "CovarianceImpl::ComputeCovarianceValuesUsingEigenSparseQR"); - if (covariance_matrix_.get() == NULL) { + if (covariance_matrix_ == nullptr) { // Nothing to do, all zeros covariance matrix. return true; } CRSMatrix jacobian; - problem_->Evaluate(evaluate_options_, NULL, NULL, NULL, &jacobian); + problem_->Evaluate(evaluate_options_, nullptr, nullptr, nullptr, &jacobian); event_logger.AddEvent("Evaluate"); - typedef Eigen::SparseMatrix<double, Eigen::ColMajor> EigenSparseMatrix; + using EigenSparseMatrix = Eigen::SparseMatrix<double, Eigen::ColMajor>; // Convert the matrix to column major order as required by SparseQR. EigenSparseMatrix sparse_jacobian = - Eigen::MappedSparseMatrix<double, Eigen::RowMajor>( + Eigen::Map<Eigen::SparseMatrix<double, Eigen::RowMajor>>( jacobian.num_rows, jacobian.num_cols, static_cast<int>(jacobian.values.size()), diff --git a/extern/ceres/internal/ceres/covariance_impl.h b/extern/ceres/internal/ceres/covariance_impl.h index 394a04bbc78..fc029ce25b7 100644 --- a/extern/ceres/internal/ceres/covariance_impl.h +++ b/extern/ceres/internal/ceres/covariance_impl.h @@ -38,7 +38,8 @@ #include <vector> #include "ceres/covariance.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/problem_impl.h" #include "ceres/suitesparse.h" @@ -47,7 +48,7 @@ namespace internal { class CompressedRowSparseMatrix; -class CERES_EXPORT_INTERNAL CovarianceImpl { +class CERES_NO_EXPORT CovarianceImpl { public: explicit CovarianceImpl(const Covariance::Options& options); ~CovarianceImpl(); @@ -98,4 +99,6 @@ class CERES_EXPORT_INTERNAL CovarianceImpl { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_COVARIANCE_IMPL_H_ diff --git a/extern/ceres/internal/ceres/cuda_buffer.h b/extern/ceres/internal/ceres/cuda_buffer.h new file mode 100644 index 00000000000..a1cf78420d5 --- /dev/null +++ b/extern/ceres/internal/ceres/cuda_buffer.h @@ -0,0 +1,107 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: joydeepb@cs.utexas.edu (Joydeep Biswas) + +#ifndef CERES_INTERNAL_CUDA_BUFFER_H_ +#define CERES_INTERNAL_CUDA_BUFFER_H_ + +#include "ceres/internal/config.h" + +#ifndef CERES_NO_CUDA + +#include <vector> + +#include "cuda_runtime.h" +#include "glog/logging.h" + +// An encapsulated buffer to maintain GPU memory, and handle transfers between +// GPU and system memory. It is the responsibility of the user to ensure that +// the appropriate GPU device is selected before each subroutine is called. This +// is particularly important when using multiple GPU devices on different CPU +// threads, since active Cuda devices are determined by the cuda runtime on a +// per-thread basis. Note that unless otherwise specified, all methods use the +// default stream, and are synchronous. +template <typename T> +class CudaBuffer { + public: + CudaBuffer() = default; + CudaBuffer(const CudaBuffer&) = delete; + CudaBuffer& operator=(const CudaBuffer&) = delete; + + ~CudaBuffer() { + if (data_ != nullptr) { + CHECK_EQ(cudaFree(data_), cudaSuccess); + } + } + + // Grow the GPU memory buffer if needed to accommodate data of the specified + // size + void Reserve(const size_t size) { + if (size > size_) { + if (data_ != nullptr) { + CHECK_EQ(cudaFree(data_), cudaSuccess); + } + CHECK_EQ(cudaMalloc(&data_, size * sizeof(T)), cudaSuccess); + size_ = size; + } + } + + // Perform an asynchronous copy from CPU memory to GPU memory using the stream + // provided. + void CopyToGpuAsync(const T* data, const size_t size, cudaStream_t stream) { + Reserve(size); + CHECK_EQ(cudaMemcpyAsync( + data_, data, size * sizeof(T), cudaMemcpyHostToDevice, stream), + cudaSuccess); + } + + // Copy data from the GPU to CPU memory. This is necessarily synchronous since + // any potential GPU kernels that may be writing to the buffer must finish + // before the transfer happens. + void CopyToHost(T* data, const size_t size) { + CHECK(data_ != nullptr); + CHECK_EQ(cudaMemcpy(data, data_, size * sizeof(T), cudaMemcpyDeviceToHost), + cudaSuccess); + } + + void CopyToGpu(const std::vector<T>& data) { + CopyToGpu(data.data(), data.size()); + } + + T* data() { return data_; } + size_t size() const { return size_; } + + private: + T* data_ = nullptr; + size_t size_ = 0; +}; + +#endif // CERES_NO_CUDA + +#endif // CERES_INTERNAL_CUDA_BUFFER_H_
\ No newline at end of file diff --git a/extern/ceres/internal/ceres/cxsparse.cc b/extern/ceres/internal/ceres/cxsparse.cc index 0167f988648..b1eb2055e35 100644 --- a/extern/ceres/internal/ceres/cxsparse.cc +++ b/extern/ceres/internal/ceres/cxsparse.cc @@ -29,10 +29,11 @@ // Author: strandmark@google.com (Petter Strandmark) // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_NO_CXSPARSE +#include <memory> #include <string> #include <vector> @@ -47,7 +48,7 @@ namespace internal { using std::vector; -CXSparse::CXSparse() : scratch_(NULL), scratch_size_(0) {} +CXSparse::CXSparse() : scratch_(nullptr), scratch_size_(0) {} CXSparse::~CXSparse() { if (scratch_size_ > 0) { @@ -116,7 +117,7 @@ cs_dis* CXSparse::BlockAnalyzeCholesky(cs_di* A, block_matrix.nzmax = block_rows.size(); block_matrix.p = &block_cols[0]; block_matrix.i = &block_rows[0]; - block_matrix.x = NULL; + block_matrix.x = nullptr; int* ordering = cs_amd(1, &block_matrix); vector<int> block_ordering(num_row_blocks, -1); @@ -126,7 +127,7 @@ cs_dis* CXSparse::BlockAnalyzeCholesky(cs_di* A, vector<int> scalar_ordering; BlockOrderingToScalarOrdering(row_blocks, block_ordering, &scalar_ordering); - cs_dis* symbolic_factor = + auto* symbolic_factor = reinterpret_cast<cs_dis*>(cs_calloc(1, sizeof(cs_dis))); symbolic_factor->pinv = cs_pinv(&scalar_ordering[0], A->n); cs* permuted_A = cs_symperm(A, symbolic_factor->pinv, 0); @@ -138,7 +139,7 @@ cs_dis* CXSparse::BlockAnalyzeCholesky(cs_di* A, cs_free(postordering); cs_spfree(permuted_A); - symbolic_factor->cp = (int*)cs_malloc(A->n + 1, sizeof(int)); + symbolic_factor->cp = static_cast<int*>(cs_malloc(A->n + 1, sizeof(int))); symbolic_factor->lnz = cs_cumsum(symbolic_factor->cp, column_counts, A->n); symbolic_factor->unz = symbolic_factor->lnz; @@ -146,7 +147,7 @@ cs_dis* CXSparse::BlockAnalyzeCholesky(cs_di* A, if (symbolic_factor->lnz < 0) { cs_sfree(symbolic_factor); - symbolic_factor = NULL; + symbolic_factor = nullptr; } return symbolic_factor; @@ -206,8 +207,8 @@ CompressedRowSparseMatrix::StorageType CXSparseCholesky::StorageType() const { CXSparseCholesky::CXSparseCholesky(const OrderingType ordering_type) : ordering_type_(ordering_type), - symbolic_factor_(NULL), - numeric_factor_(NULL) {} + symbolic_factor_(nullptr), + numeric_factor_(nullptr) {} CXSparseCholesky::~CXSparseCholesky() { FreeSymbolicFactorization(); @@ -217,14 +218,14 @@ CXSparseCholesky::~CXSparseCholesky() { LinearSolverTerminationType CXSparseCholesky::Factorize( CompressedRowSparseMatrix* lhs, std::string* message) { CHECK_EQ(lhs->storage_type(), StorageType()); - if (lhs == NULL) { - *message = "Failure: Input lhs is NULL."; + if (lhs == nullptr) { + *message = "Failure: Input lhs is nullptr."; return LINEAR_SOLVER_FATAL_ERROR; } cs_di cs_lhs = cs_.CreateSparseMatrixTransposeView(lhs); - if (symbolic_factor_ == NULL) { + if (symbolic_factor_ == nullptr) { if (ordering_type_ == NATURAL) { symbolic_factor_ = cs_.AnalyzeCholeskyWithNaturalOrdering(&cs_lhs); } else { @@ -236,7 +237,7 @@ LinearSolverTerminationType CXSparseCholesky::Factorize( } } - if (symbolic_factor_ == NULL) { + if (symbolic_factor_ == nullptr) { *message = "CXSparse Failure : Symbolic factorization failed."; return LINEAR_SOLVER_FATAL_ERROR; } @@ -244,7 +245,7 @@ LinearSolverTerminationType CXSparseCholesky::Factorize( FreeNumericFactorization(); numeric_factor_ = cs_.Cholesky(&cs_lhs, symbolic_factor_); - if (numeric_factor_ == NULL) { + if (numeric_factor_ == nullptr) { *message = "CXSparse Failure : Numeric factorization failed."; return LINEAR_SOLVER_FAILURE; } @@ -255,7 +256,7 @@ LinearSolverTerminationType CXSparseCholesky::Factorize( LinearSolverTerminationType CXSparseCholesky::Solve(const double* rhs, double* solution, std::string* message) { - CHECK(numeric_factor_ != NULL) + CHECK(numeric_factor_ != nullptr) << "Solve called without a call to Factorize first."; const int num_cols = numeric_factor_->L->n; memcpy(solution, rhs, num_cols * sizeof(*solution)); @@ -264,16 +265,16 @@ LinearSolverTerminationType CXSparseCholesky::Solve(const double* rhs, } void CXSparseCholesky::FreeSymbolicFactorization() { - if (symbolic_factor_ != NULL) { + if (symbolic_factor_ != nullptr) { cs_.Free(symbolic_factor_); - symbolic_factor_ = NULL; + symbolic_factor_ = nullptr; } } void CXSparseCholesky::FreeNumericFactorization() { - if (numeric_factor_ != NULL) { + if (numeric_factor_ != nullptr) { cs_.Free(numeric_factor_); - numeric_factor_ = NULL; + numeric_factor_ = nullptr; } } diff --git a/extern/ceres/internal/ceres/cxsparse.h b/extern/ceres/internal/ceres/cxsparse.h index d3f76e0575e..97fc0459464 100644 --- a/extern/ceres/internal/ceres/cxsparse.h +++ b/extern/ceres/internal/ceres/cxsparse.h @@ -32,7 +32,7 @@ #define CERES_INTERNAL_CXSPARSE_H_ // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_NO_CXSPARSE @@ -40,6 +40,7 @@ #include <string> #include <vector> +#include "ceres/internal/disable_warnings.h" #include "ceres/linear_solver.h" #include "ceres/sparse_cholesky.h" #include "cs.h" @@ -54,7 +55,7 @@ class TripletSparseMatrix; // factorization with a known symbolic factorization. This features does not // explicitly exist in CXSparse. The methods in the class are nonstatic because // the class manages internal scratch space. -class CXSparse { +class CERES_NO_EXPORT CXSparse { public: CXSparse(); ~CXSparse(); @@ -80,7 +81,7 @@ class CXSparse { cs_di CreateSparseMatrixTransposeView(CompressedRowSparseMatrix* A); // Creates a new matrix from a triplet form. Deallocate the returned matrix - // with Free. May return NULL if the compression or allocation fails. + // with Free. May return nullptr if the compression or allocation fails. cs_di* CreateSparseMatrix(TripletSparseMatrix* A); // B = A' @@ -122,7 +123,7 @@ class CXSparse { const std::vector<int>& col_blocks); // Compute an fill-reducing approximate minimum degree ordering of - // the matrix A. ordering should be non-NULL and should point to + // the matrix A. ordering should be non-nullptr and should point to // enough memory to hold the ordering for the rows of A. void ApproximateMinimumDegreeOrdering(cs_di* A, int* ordering); @@ -138,13 +139,13 @@ class CXSparse { // An implementation of SparseCholesky interface using the CXSparse // library. -class CXSparseCholesky : public SparseCholesky { +class CERES_NO_EXPORT CXSparseCholesky final : public SparseCholesky { public: // Factory static std::unique_ptr<SparseCholesky> Create(OrderingType ordering_type); // SparseCholesky interface. - virtual ~CXSparseCholesky(); + ~CXSparseCholesky() override; CompressedRowSparseMatrix::StorageType StorageType() const final; LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, std::string* message) final; @@ -153,7 +154,7 @@ class CXSparseCholesky : public SparseCholesky { std::string* message) final; private: - CXSparseCholesky(const OrderingType ordering_type); + explicit CXSparseCholesky(const OrderingType ordering_type); void FreeSymbolicFactorization(); void FreeNumericFactorization(); @@ -166,6 +167,8 @@ class CXSparseCholesky : public SparseCholesky { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #else typedef void cs_dis; diff --git a/extern/ceres/internal/ceres/dense_cholesky.cc b/extern/ceres/internal/ceres/dense_cholesky.cc new file mode 100644 index 00000000000..0e0bba7873b --- /dev/null +++ b/extern/ceres/internal/ceres/dense_cholesky.cc @@ -0,0 +1,327 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#include "ceres/dense_cholesky.h" + +#include <algorithm> +#include <memory> +#include <string> +#include <vector> + +#include "ceres/internal/config.h" + +#ifndef CERES_NO_CUDA +#include "ceres/context_impl.h" +#include "cuda_runtime.h" +#include "cusolverDn.h" +#endif // CERES_NO_CUDA + +#ifndef CERES_NO_LAPACK + +// C interface to the LAPACK Cholesky factorization and triangular solve. +extern "C" void dpotrf_( + const char* uplo, const int* n, double* a, const int* lda, int* info); + +extern "C" void dpotrs_(const char* uplo, + const int* n, + const int* nrhs, + const double* a, + const int* lda, + double* b, + const int* ldb, + int* info); +#endif + +namespace ceres { +namespace internal { + +DenseCholesky::~DenseCholesky() = default; + +std::unique_ptr<DenseCholesky> DenseCholesky::Create( + const LinearSolver::Options& options) { + std::unique_ptr<DenseCholesky> dense_cholesky; + + switch (options.dense_linear_algebra_library_type) { + case EIGEN: + dense_cholesky = std::make_unique<EigenDenseCholesky>(); + break; + + case LAPACK: +#ifndef CERES_NO_LAPACK + dense_cholesky = std::make_unique<LAPACKDenseCholesky>(); + break; +#else + LOG(FATAL) << "Ceres was compiled without support for LAPACK."; +#endif + + case CUDA: +#ifndef CERES_NO_CUDA + dense_cholesky = CUDADenseCholesky::Create(options); + break; +#else + LOG(FATAL) << "Ceres was compiled without support for CUDA."; +#endif + + default: + LOG(FATAL) << "Unknown dense linear algebra library type : " + << DenseLinearAlgebraLibraryTypeToString( + options.dense_linear_algebra_library_type); + } + return dense_cholesky; +} + +LinearSolverTerminationType DenseCholesky::FactorAndSolve( + int num_cols, + double* lhs, + const double* rhs, + double* solution, + std::string* message) { + LinearSolverTerminationType termination_type = + Factorize(num_cols, lhs, message); + if (termination_type == LINEAR_SOLVER_SUCCESS) { + termination_type = Solve(rhs, solution, message); + } + return termination_type; +} + +LinearSolverTerminationType EigenDenseCholesky::Factorize( + int num_cols, double* lhs, std::string* message) { + Eigen::Map<Eigen::MatrixXd> m(lhs, num_cols, num_cols); + llt_ = std::make_unique<LLTType>(m); + if (llt_->info() != Eigen::Success) { + *message = "Eigen failure. Unable to perform dense Cholesky factorization."; + return LINEAR_SOLVER_FAILURE; + } + + *message = "Success."; + return LINEAR_SOLVER_SUCCESS; +} + +LinearSolverTerminationType EigenDenseCholesky::Solve(const double* rhs, + double* solution, + std::string* message) { + if (llt_->info() != Eigen::Success) { + *message = "Eigen failure. Unable to perform dense Cholesky factorization."; + return LINEAR_SOLVER_FAILURE; + } + + VectorRef(solution, llt_->cols()) = + llt_->solve(ConstVectorRef(rhs, llt_->cols())); + *message = "Success."; + return LINEAR_SOLVER_SUCCESS; +} + +#ifndef CERES_NO_LAPACK +LinearSolverTerminationType LAPACKDenseCholesky::Factorize( + int num_cols, double* lhs, std::string* message) { + lhs_ = lhs; + num_cols_ = num_cols; + + const char uplo = 'L'; + int info = 0; + dpotrf_(&uplo, &num_cols_, lhs_, &num_cols_, &info); + + if (info < 0) { + termination_type_ = LINEAR_SOLVER_FATAL_ERROR; + LOG(FATAL) << "Congratulations, you found a bug in Ceres. " + << "Please report it. " + << "LAPACK::dpotrf fatal error. " + << "Argument: " << -info << " is invalid."; + } else if (info > 0) { + termination_type_ = LINEAR_SOLVER_FAILURE; + *message = StringPrintf( + "LAPACK::dpotrf numerical failure. " + "The leading minor of order %d is not positive definite.", + info); + } else { + termination_type_ = LINEAR_SOLVER_SUCCESS; + *message = "Success."; + } + return termination_type_; +} + +LinearSolverTerminationType LAPACKDenseCholesky::Solve(const double* rhs, + double* solution, + std::string* message) { + const char uplo = 'L'; + const int nrhs = 1; + int info = 0; + + std::copy_n(rhs, num_cols_, solution); + dpotrs_( + &uplo, &num_cols_, &nrhs, lhs_, &num_cols_, solution, &num_cols_, &info); + + if (info < 0) { + termination_type_ = LINEAR_SOLVER_FATAL_ERROR; + LOG(FATAL) << "Congratulations, you found a bug in Ceres. " + << "Please report it. " + << "LAPACK::dpotrs fatal error. " + << "Argument: " << -info << " is invalid."; + } + + *message = "Success"; + termination_type_ = LINEAR_SOLVER_SUCCESS; + + return termination_type_; +} + +#endif // CERES_NO_LAPACK + +#ifndef CERES_NO_CUDA + +bool CUDADenseCholesky::Init(ContextImpl* context, std::string* message) { + if (!context->InitCUDA(message)) { + return false; + } + cusolver_handle_ = context->cusolver_handle_; + stream_ = context->stream_; + error_.Reserve(1); + *message = "CUDADenseCholesky::Init Success."; + return true; +} + +LinearSolverTerminationType CUDADenseCholesky::Factorize(int num_cols, + double* lhs, + std::string* message) { + factorize_result_ = LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + lhs_.Reserve(num_cols * num_cols); + num_cols_ = num_cols; + lhs_.CopyToGpuAsync(lhs, num_cols * num_cols, stream_); + int device_workspace_size = 0; + if (cusolverDnDpotrf_bufferSize(cusolver_handle_, + CUBLAS_FILL_MODE_LOWER, + num_cols, + lhs_.data(), + num_cols, + &device_workspace_size) != + CUSOLVER_STATUS_SUCCESS) { + *message = "cuSolverDN::cusolverDnDpotrf_bufferSize failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + device_workspace_.Reserve(device_workspace_size); + if (cusolverDnDpotrf(cusolver_handle_, + CUBLAS_FILL_MODE_LOWER, + num_cols, + lhs_.data(), + num_cols, + reinterpret_cast<double*>(device_workspace_.data()), + device_workspace_.size(), + error_.data()) != CUSOLVER_STATUS_SUCCESS) { + *message = "cuSolverDN::cusolverDnDpotrf failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + if (cudaDeviceSynchronize() != cudaSuccess || + cudaStreamSynchronize(stream_) != cudaSuccess) { + *message = "Cuda device synchronization failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + int error = 0; + error_.CopyToHost(&error, 1); + if (error < 0) { + LOG(FATAL) << "Congratulations, you found a bug in Ceres - " + << "please report it. " + << "cuSolverDN::cusolverDnXpotrf fatal error. " + << "Argument: " << -error << " is invalid."; + // The following line is unreachable, but return failure just to be + // pedantic, since the compiler does not know that. + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } else if (error > 0) { + *message = StringPrintf( + "cuSolverDN::cusolverDnDpotrf numerical failure. " + "The leading minor of order %d is not positive definite.", + error); + factorize_result_ = LinearSolverTerminationType::LINEAR_SOLVER_FAILURE; + return LinearSolverTerminationType::LINEAR_SOLVER_FAILURE; + } + *message = "Success"; + factorize_result_ = LinearSolverTerminationType::LINEAR_SOLVER_SUCCESS; + return LinearSolverTerminationType::LINEAR_SOLVER_SUCCESS; +} + +LinearSolverTerminationType CUDADenseCholesky::Solve(const double* rhs, + double* solution, + std::string* message) { + if (factorize_result_ != LinearSolverTerminationType::LINEAR_SOLVER_SUCCESS) { + *message = "Factorize did not complete succesfully previously."; + return factorize_result_; + } + rhs_.CopyToGpuAsync(rhs, num_cols_, stream_); + if (cusolverDnDpotrs(cusolver_handle_, + CUBLAS_FILL_MODE_LOWER, + num_cols_, + 1, + lhs_.data(), + num_cols_, + rhs_.data(), + num_cols_, + error_.data()) != CUSOLVER_STATUS_SUCCESS) { + *message = "cuSolverDN::cusolverDnDpotrs failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + if (cudaDeviceSynchronize() != cudaSuccess || + cudaStreamSynchronize(stream_) != cudaSuccess) { + *message = "Cuda device synchronization failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + int error = 0; + error_.CopyToHost(&error, 1); + if (error != 0) { + LOG(FATAL) << "Congratulations, you found a bug in Ceres. " + << "Please report it." + << "cuSolverDN::cusolverDnDpotrs fatal error. " + << "Argument: " << -error << " is invalid."; + } + rhs_.CopyToHost(solution, num_cols_); + *message = "Success"; + return LinearSolverTerminationType::LINEAR_SOLVER_SUCCESS; +} + +std::unique_ptr<CUDADenseCholesky> CUDADenseCholesky::Create( + const LinearSolver::Options& options) { + if (options.dense_linear_algebra_library_type != CUDA) { + // The user called the wrong factory method. + return nullptr; + } + auto cuda_dense_cholesky = + std::unique_ptr<CUDADenseCholesky>(new CUDADenseCholesky()); + std::string cuda_error; + if (cuda_dense_cholesky->Init(options.context, &cuda_error)) { + return cuda_dense_cholesky; + } + // Initialization failed, destroy the object (done automatically) and return a + // nullptr. + LOG(ERROR) << "CUDADenseCholesky::Init failed: " << cuda_error; + return nullptr; +} + +#endif // CERES_NO_CUDA + +} // namespace internal +} // namespace ceres diff --git a/extern/ceres/internal/ceres/dense_cholesky.h b/extern/ceres/internal/ceres/dense_cholesky.h new file mode 100644 index 00000000000..655a2f815ee --- /dev/null +++ b/extern/ceres/internal/ceres/dense_cholesky.h @@ -0,0 +1,183 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#ifndef CERES_INTERNAL_DENSE_CHOLESKY_H_ +#define CERES_INTERNAL_DENSE_CHOLESKY_H_ + +// This include must come before any #ifndef check on Ceres compile options. +// clang-format off +#include "ceres/internal/config.h" +// clang-format on + +#include <memory> +#include <vector> + +#include "Eigen/Dense" +#include "ceres/cuda_buffer.h" +#include "ceres/linear_solver.h" +#include "glog/logging.h" +#ifndef CERES_NO_CUDA +#include "ceres/context_impl.h" +#include "cuda_runtime.h" +#include "cusolverDn.h" +#endif // CERES_NO_CUDA + +namespace ceres { +namespace internal { + +// An interface that abstracts away the internal details of various dense linear +// algebra libraries and offers a simple API for solving dense symmetric +// positive definite linear systems using a Cholesky factorization. +class CERES_NO_EXPORT DenseCholesky { + public: + static std::unique_ptr<DenseCholesky> Create( + const LinearSolver::Options& options); + + virtual ~DenseCholesky(); + + // Computes the Cholesky factorization of the given matrix. + // + // The input matrix lhs is assumed to be a column-major num_cols x num_cols + // matrix, that is symmetric positive definite with its lower triangular part + // containing the left hand side of the linear system being solved. + // + // The input matrix lhs may be modified by the implementation to store the + // factorization, irrespective of whether the factorization succeeds or not. + // As a result it is the user's responsibility to ensure that lhs is valid + // when Solve is called. + virtual LinearSolverTerminationType Factorize(int num_cols, + double* lhs, + std::string* message) = 0; + + // Computes the solution to the equation + // + // lhs * solution = rhs + // + // Calling Solve without calling Factorize is undefined behaviour. It is the + // user's responsibility to ensure that the input matrix lhs passed to + // Factorize has not been freed/modified when Solve is called. + virtual LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) = 0; + + // Convenience method which combines a call to Factorize and Solve. Solve is + // only called if Factorize returns LINEAR_SOLVER_SUCCESS. + // + // The input matrix lhs may be modified by the implementation to store the + // factorization, irrespective of whether the method succeeds or not. It is + // the user's responsibility to ensure that lhs is valid if and when Solve is + // called again after this call. + LinearSolverTerminationType FactorAndSolve(int num_cols, + double* lhs, + const double* rhs, + double* solution, + std::string* message); +}; + +class CERES_NO_EXPORT EigenDenseCholesky final : public DenseCholesky { + public: + LinearSolverTerminationType Factorize(int num_cols, + double* lhs, + std::string* message) override; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override; + + private: + using LLTType = Eigen::LLT<Eigen::Ref<Eigen::MatrixXd>, Eigen::Lower>; + std::unique_ptr<LLTType> llt_; +}; + +#ifndef CERES_NO_LAPACK +class CERES_NO_EXPORT LAPACKDenseCholesky final : public DenseCholesky { + public: + LinearSolverTerminationType Factorize(int num_cols, + double* lhs, + std::string* message) override; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override; + + private: + double* lhs_ = nullptr; + int num_cols_ = -1; + LinearSolverTerminationType termination_type_ = LINEAR_SOLVER_FATAL_ERROR; +}; +#endif // CERES_NO_LAPACK + +#ifndef CERES_NO_CUDA +// CUDA implementation of DenseCholesky using the cuSolverDN library using the +// 32-bit legacy interface for maximum compatibility. +class CERES_NO_EXPORT CUDADenseCholesky final : public DenseCholesky { + public: + static std::unique_ptr<CUDADenseCholesky> Create( + const LinearSolver::Options& options); + CUDADenseCholesky(const CUDADenseCholesky&) = delete; + CUDADenseCholesky& operator=(const CUDADenseCholesky&) = delete; + LinearSolverTerminationType Factorize(int num_cols, + double* lhs, + std::string* message) override; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override; + + private: + CUDADenseCholesky() = default; + // Picks up the cuSolverDN and cuStream handles from the context. If + // the context is unable to initialize CUDA, returns false with a + // human-readable message indicating the reason. + bool Init(ContextImpl* context, std::string* message); + + // Handle to the cuSOLVER context. + cusolverDnHandle_t cusolver_handle_ = nullptr; + // CUDA device stream. + cudaStream_t stream_ = nullptr; + // Number of columns in the A matrix, to be cached between calls to *Factorize + // and *Solve. + size_t num_cols_ = 0; + // GPU memory allocated for the A matrix (lhs matrix). + CudaBuffer<double> lhs_; + // GPU memory allocated for the B matrix (rhs vector). + CudaBuffer<double> rhs_; + // Scratch space for cuSOLVER on the GPU. + CudaBuffer<double> device_workspace_; + // Required for error handling with cuSOLVER. + CudaBuffer<int> error_; + // Cache the result of Factorize to ensure that when Solve is called, the + // factiorization of lhs is valid. + LinearSolverTerminationType factorize_result_ = LINEAR_SOLVER_FATAL_ERROR; +}; + +#endif // CERES_NO_CUDA + +} // namespace internal +} // namespace ceres + +#endif // CERES_INTERNAL_DENSE_CHOLESKY_H_ diff --git a/extern/ceres/internal/ceres/dense_jacobian_writer.h b/extern/ceres/internal/ceres/dense_jacobian_writer.h index 28c60e20a1b..0020937124e 100644 --- a/extern/ceres/internal/ceres/dense_jacobian_writer.h +++ b/extern/ceres/internal/ceres/dense_jacobian_writer.h @@ -33,9 +33,13 @@ #ifndef CERES_INTERNAL_DENSE_JACOBIAN_WRITER_H_ #define CERES_INTERNAL_DENSE_JACOBIAN_WRITER_H_ +#include <memory> + #include "ceres/casts.h" #include "ceres/dense_sparse_matrix.h" +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" #include "ceres/parameter_block.h" #include "ceres/program.h" #include "ceres/residual_block.h" @@ -44,7 +48,7 @@ namespace ceres { namespace internal { -class DenseJacobianWriter { +class CERES_NO_EXPORT DenseJacobianWriter { public: DenseJacobianWriter(Evaluator::Options /* ignored */, Program* program) : program_(program) {} @@ -54,13 +58,14 @@ class DenseJacobianWriter { // Since the dense matrix has different layout than that assumed by the cost // functions, use scratch space to store the jacobians temporarily then copy // them over to the larger jacobian later. - ScratchEvaluatePreparer* CreateEvaluatePreparers(int num_threads) { + std::unique_ptr<ScratchEvaluatePreparer[]> CreateEvaluatePreparers( + int num_threads) { return ScratchEvaluatePreparer::Create(*program_, num_threads); } - SparseMatrix* CreateJacobian() const { - return new DenseSparseMatrix( - program_->NumResiduals(), program_->NumEffectiveParameters(), true); + std::unique_ptr<SparseMatrix> CreateJacobian() const { + return std::make_unique<DenseSparseMatrix>( + program_->NumResiduals(), program_->NumEffectiveParameters()); } void Write(int residual_id, @@ -82,14 +87,14 @@ class DenseJacobianWriter { continue; } - const int parameter_block_size = parameter_block->LocalSize(); + const int parameter_block_size = parameter_block->TangentSize(); ConstMatrixRef parameter_jacobian( jacobians[j], num_residuals, parameter_block_size); - dense_jacobian->mutable_matrix().block(residual_offset, - parameter_block->delta_offset(), - num_residuals, - parameter_block_size) = + dense_jacobian->mutable_matrix()->block(residual_offset, + parameter_block->delta_offset(), + num_residuals, + parameter_block_size) = parameter_jacobian; } } @@ -101,4 +106,6 @@ class DenseJacobianWriter { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_DENSE_JACOBIAN_WRITER_H_ diff --git a/extern/ceres/internal/ceres/dense_normal_cholesky_solver.cc b/extern/ceres/internal/ceres/dense_normal_cholesky_solver.cc index 51c639097b6..30a0c023f51 100644 --- a/extern/ceres/internal/ceres/dense_normal_cholesky_solver.cc +++ b/extern/ceres/internal/ceres/dense_normal_cholesky_solver.cc @@ -30,13 +30,11 @@ #include "ceres/dense_normal_cholesky_solver.h" -#include <cstddef> +#include <utility> #include "Eigen/Dense" -#include "ceres/blas.h" #include "ceres/dense_sparse_matrix.h" #include "ceres/internal/eigen.h" -#include "ceres/lapack.h" #include "ceres/linear_solver.h" #include "ceres/types.h" #include "ceres/wall_time.h" @@ -45,32 +43,20 @@ namespace ceres { namespace internal { DenseNormalCholeskySolver::DenseNormalCholeskySolver( - const LinearSolver::Options& options) - : options_(options) {} + LinearSolver::Options options) + : options_(std::move(options)), + cholesky_(DenseCholesky::Create(options_)) {} LinearSolver::Summary DenseNormalCholeskySolver::SolveImpl( DenseSparseMatrix* A, const double* b, const LinearSolver::PerSolveOptions& per_solve_options, double* x) { - if (options_.dense_linear_algebra_library_type == EIGEN) { - return SolveUsingEigen(A, b, per_solve_options, x); - } else { - return SolveUsingLAPACK(A, b, per_solve_options, x); - } -} - -LinearSolver::Summary DenseNormalCholeskySolver::SolveUsingEigen( - DenseSparseMatrix* A, - const double* b, - const LinearSolver::PerSolveOptions& per_solve_options, - double* x) { EventLogger event_logger("DenseNormalCholeskySolver::Solve"); const int num_rows = A->num_rows(); const int num_cols = A->num_cols(); - ConstColMajorMatrixRef Aref = A->matrix(); Matrix lhs(num_cols, num_cols); lhs.setZero(); @@ -81,12 +67,12 @@ LinearSolver::Summary DenseNormalCholeskySolver::SolveUsingEigen( // Using rankUpdate instead of GEMM, exposes the fact that its the // same matrix being multiplied with itself and that the product is // symmetric. - lhs.selfadjointView<Eigen::Upper>().rankUpdate(Aref.transpose()); + lhs.selfadjointView<Eigen::Upper>().rankUpdate(A->matrix().transpose()); // rhs = A'b - Vector rhs = Aref.transpose() * ConstVectorRef(b, num_rows); + Vector rhs = A->matrix().transpose() * ConstVectorRef(b, num_rows); - if (per_solve_options.D != NULL) { + if (per_solve_options.D != nullptr) { ConstVectorRef D(per_solve_options.D, num_cols); lhs += D.array().square().matrix().asDiagonal(); } @@ -94,64 +80,12 @@ LinearSolver::Summary DenseNormalCholeskySolver::SolveUsingEigen( LinearSolver::Summary summary; summary.num_iterations = 1; - summary.termination_type = LINEAR_SOLVER_SUCCESS; - Eigen::LLT<Matrix, Eigen::Upper> llt = - lhs.selfadjointView<Eigen::Upper>().llt(); - - if (llt.info() != Eigen::Success) { - summary.termination_type = LINEAR_SOLVER_FAILURE; - summary.message = "Eigen LLT decomposition failed."; - } else { - summary.termination_type = LINEAR_SOLVER_SUCCESS; - summary.message = "Success."; - } + summary.termination_type = cholesky_->FactorAndSolve( + num_cols, lhs.data(), rhs.data(), x, &summary.message); + event_logger.AddEvent("FactorAndSolve"); - VectorRef(x, num_cols) = llt.solve(rhs); - event_logger.AddEvent("Solve"); return summary; } -LinearSolver::Summary DenseNormalCholeskySolver::SolveUsingLAPACK( - DenseSparseMatrix* A, - const double* b, - const LinearSolver::PerSolveOptions& per_solve_options, - double* x) { - EventLogger event_logger("DenseNormalCholeskySolver::Solve"); - - if (per_solve_options.D != NULL) { - // Temporarily append a diagonal block to the A matrix, but undo - // it before returning the matrix to the user. - A->AppendDiagonal(per_solve_options.D); - } - - const int num_cols = A->num_cols(); - Matrix lhs(num_cols, num_cols); - event_logger.AddEvent("Setup"); - - // lhs = A'A - // - // Note: This is a bit delicate, it assumes that the stride on this - // matrix is the same as the number of rows. - BLAS::SymmetricRankKUpdate( - A->num_rows(), num_cols, A->values(), true, 1.0, 0.0, lhs.data()); - - if (per_solve_options.D != NULL) { - // Undo the modifications to the matrix A. - A->RemoveDiagonal(); - } - - // TODO(sameeragarwal): Replace this with a gemv call for true blasness. - // rhs = A'b - VectorRef(x, num_cols) = - A->matrix().transpose() * ConstVectorRef(b, A->num_rows()); - event_logger.AddEvent("Product"); - - LinearSolver::Summary summary; - summary.num_iterations = 1; - summary.termination_type = LAPACK::SolveInPlaceUsingCholesky( - num_cols, lhs.data(), x, &summary.message); - event_logger.AddEvent("Solve"); - return summary; -} } // namespace internal } // namespace ceres diff --git a/extern/ceres/internal/ceres/dense_normal_cholesky_solver.h b/extern/ceres/internal/ceres/dense_normal_cholesky_solver.h index 68ea611299f..5b3c74069f0 100644 --- a/extern/ceres/internal/ceres/dense_normal_cholesky_solver.h +++ b/extern/ceres/internal/ceres/dense_normal_cholesky_solver.h @@ -34,6 +34,11 @@ #ifndef CERES_INTERNAL_DENSE_NORMAL_CHOLESKY_SOLVER_H_ #define CERES_INTERNAL_DENSE_NORMAL_CHOLESKY_SOLVER_H_ +#include <memory> + +#include "ceres/dense_cholesky.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { @@ -73,9 +78,10 @@ class DenseSparseMatrix; // library. This solver always returns a solution, it is the user's // responsibility to judge if the solution is good enough for their // purposes. -class DenseNormalCholeskySolver : public DenseSparseMatrixSolver { +class CERES_NO_EXPORT DenseNormalCholeskySolver + : public DenseSparseMatrixSolver { public: - explicit DenseNormalCholeskySolver(const LinearSolver::Options& options); + explicit DenseNormalCholeskySolver(LinearSolver::Options options); private: LinearSolver::Summary SolveImpl( @@ -84,22 +90,13 @@ class DenseNormalCholeskySolver : public DenseSparseMatrixSolver { const LinearSolver::PerSolveOptions& per_solve_options, double* x) final; - LinearSolver::Summary SolveUsingLAPACK( - DenseSparseMatrix* A, - const double* b, - const LinearSolver::PerSolveOptions& per_solve_options, - double* x); - - LinearSolver::Summary SolveUsingEigen( - DenseSparseMatrix* A, - const double* b, - const LinearSolver::PerSolveOptions& per_solve_options, - double* x); - const LinearSolver::Options options_; + std::unique_ptr<DenseCholesky> cholesky_; }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_DENSE_NORMAL_CHOLESKY_SOLVER_H_ diff --git a/extern/ceres/internal/ceres/dense_qr.cc b/extern/ceres/internal/ceres/dense_qr.cc new file mode 100644 index 00000000000..4b9c8a4a035 --- /dev/null +++ b/extern/ceres/internal/ceres/dense_qr.cc @@ -0,0 +1,481 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#include "ceres/dense_qr.h" + +#include <algorithm> +#include <memory> +#include <string> +#ifndef CERES_NO_CUDA +#include "ceres/context_impl.h" +#include "cublas_v2.h" +#include "cusolverDn.h" +#endif // CERES_NO_CUDA + +#ifndef CERES_NO_LAPACK + +// LAPACK routines for solving a linear least squares problem using QR +// factorization. This is done in three stages: +// +// A * x = b +// Q * R * x = b (dgeqrf) +// R * x = Q' * b (dormqr) +// x = R^{-1} * Q'* b (dtrtrs) + +// clang-format off + +// Compute the QR factorization of a. +// +// a is an m x n column major matrix (Denoted by "A" in the above description) +// lda is the leading dimension of a. lda >= max(1, num_rows) +// tau is an array of size min(m,n). It contains the scalar factors of the +// elementary reflectors. +// work is an array of size max(1,lwork). On exit, if info=0, work[0] contains +// the optimal size of work. +// +// if lwork >= 1 it is the size of work. If lwork = -1, then a workspace query is assumed. +// dgeqrf computes the optimal size of the work array and returns it as work[0]. +// +// info = 0, successful exit. +// info < 0, if info = -i, then the i^th argument had illegal value. +extern "C" void dgeqrf_(const int* m, const int* n, double* a, const int* lda, + double* tau, double* work, const int* lwork, int* info); + +// Apply Q or Q' to b. +// +// b is a m times n column major matrix. +// size = 'L' applies Q or Q' on the left, size = 'R' applies Q or Q' on the right. +// trans = 'N', applies Q, trans = 'T', applies Q'. +// k is the number of elementary reflectors whose product defines the matrix Q. +// If size = 'L', m >= k >= 0 and if side = 'R', n >= k >= 0. +// a is an lda x k column major matrix containing the reflectors as returned by dgeqrf. +// ldb is the leading dimension of b. +// work is an array of size max(1, lwork) +// lwork if positive is the size of work. If lwork = -1, then a +// workspace query is assumed. +// +// info = 0, successful exit. +// info < 0, if info = -i, then the i^th argument had illegal value. +extern "C" void dormqr_(const char* side, const char* trans, const int* m, + const int* n ,const int* k, double* a, const int* lda, + double* tau, double* b, const int* ldb, double* work, + const int* lwork, int* info); + +// Solve a triangular system of the form A * x = b +// +// uplo = 'U', A is upper triangular. uplo = 'L' is lower triangular. +// trans = 'N', 'T', 'C' specifies the form - A, A^T, A^H. +// DIAG = 'N', A is not unit triangular. 'U' is unit triangular. +// n is the order of the matrix A. +// nrhs number of columns of b. +// a is a column major lda x n. +// b is a column major matrix of ldb x nrhs +// +// info = 0 succesful. +// = -i < 0 i^th argument is an illegal value. +// = i > 0, i^th diagonal element of A is zero. +extern "C" void dtrtrs_(const char* uplo, const char* trans, const char* diag, + const int* n, const int* nrhs, double* a, const int* lda, + double* b, const int* ldb, int* info); +// clang-format on + +#endif + +namespace ceres { +namespace internal { + +DenseQR::~DenseQR() = default; + +std::unique_ptr<DenseQR> DenseQR::Create(const LinearSolver::Options& options) { + std::unique_ptr<DenseQR> dense_qr; + + switch (options.dense_linear_algebra_library_type) { + case EIGEN: + dense_qr = std::make_unique<EigenDenseQR>(); + break; + + case LAPACK: +#ifndef CERES_NO_LAPACK + dense_qr = std::make_unique<LAPACKDenseQR>(); + break; +#else + LOG(FATAL) << "Ceres was compiled without support for LAPACK."; +#endif + + case CUDA: +#ifndef CERES_NO_CUDA + dense_qr = CUDADenseQR::Create(options); + break; +#else + LOG(FATAL) << "Ceres was compiled without support for CUDA."; +#endif + + default: + LOG(FATAL) << "Unknown dense linear algebra library type : " + << DenseLinearAlgebraLibraryTypeToString( + options.dense_linear_algebra_library_type); + } + return dense_qr; +} + +LinearSolverTerminationType DenseQR::FactorAndSolve(int num_rows, + int num_cols, + double* lhs, + const double* rhs, + double* solution, + std::string* message) { + LinearSolverTerminationType termination_type = + Factorize(num_rows, num_cols, lhs, message); + if (termination_type == LINEAR_SOLVER_SUCCESS) { + termination_type = Solve(rhs, solution, message); + } + return termination_type; +} + +LinearSolverTerminationType EigenDenseQR::Factorize(int num_rows, + int num_cols, + double* lhs, + std::string* message) { + Eigen::Map<ColMajorMatrix> m(lhs, num_rows, num_cols); + qr_ = std::make_unique<QRType>(m); + *message = "Success."; + return LINEAR_SOLVER_SUCCESS; +} + +LinearSolverTerminationType EigenDenseQR::Solve(const double* rhs, + double* solution, + std::string* message) { + VectorRef(solution, qr_->cols()) = + qr_->solve(ConstVectorRef(rhs, qr_->rows())); + *message = "Success."; + return LINEAR_SOLVER_SUCCESS; +} + +#ifndef CERES_NO_LAPACK +LinearSolverTerminationType LAPACKDenseQR::Factorize(int num_rows, + int num_cols, + double* lhs, + std::string* message) { + int lwork = -1; + double work_size; + int info = 0; + + // Compute the size of the temporary workspace needed to compute the QR + // factorization in the dgeqrf call below. + dgeqrf_(&num_rows, + &num_cols, + lhs_, + &num_rows, + tau_.data(), + &work_size, + &lwork, + &info); + if (info < 0) { + LOG(FATAL) << "Congratulations, you found a bug in Ceres." + << "Please report it." + << "LAPACK::dgels fatal error." + << "Argument: " << -info << " is invalid."; + } + + lhs_ = lhs; + num_rows_ = num_rows; + num_cols_ = num_cols; + + lwork = static_cast<int>(work_size); + + if (work_.size() < lwork) { + work_.resize(lwork); + } + if (tau_.size() < num_cols) { + tau_.resize(num_cols); + } + + if (q_transpose_rhs_.size() < num_rows) { + q_transpose_rhs_.resize(num_rows); + } + + // Factorize the lhs_ using the workspace that we just constructed above. + dgeqrf_(&num_rows, + &num_cols, + lhs_, + &num_rows, + tau_.data(), + work_.data(), + &lwork, + &info); + + if (info < 0) { + LOG(FATAL) << "Congratulations, you found a bug in Ceres." + << "Please report it. dgeqrf fatal error." + << "Argument: " << -info << " is invalid."; + } + + termination_type_ = LINEAR_SOLVER_SUCCESS; + *message = "Success."; + return termination_type_; +} + +LinearSolverTerminationType LAPACKDenseQR::Solve(const double* rhs, + double* solution, + std::string* message) { + if (termination_type_ != LINEAR_SOLVER_SUCCESS) { + *message = "QR factorization failed and solve called."; + return termination_type_; + } + + std::copy_n(rhs, num_rows_, q_transpose_rhs_.data()); + + const char side = 'L'; + char trans = 'T'; + const int num_c_cols = 1; + const int lwork = work_.size(); + int info = 0; + dormqr_(&side, + &trans, + &num_rows_, + &num_c_cols, + &num_cols_, + lhs_, + &num_rows_, + tau_.data(), + q_transpose_rhs_.data(), + &num_rows_, + work_.data(), + &lwork, + &info); + if (info < 0) { + LOG(FATAL) << "Congratulations, you found a bug in Ceres." + << "Please report it. dormr fatal error." + << "Argument: " << -info << " is invalid."; + } + + const char uplo = 'U'; + trans = 'N'; + const char diag = 'N'; + dtrtrs_(&uplo, + &trans, + &diag, + &num_cols_, + &num_c_cols, + lhs_, + &num_rows_, + q_transpose_rhs_.data(), + &num_rows_, + &info); + + if (info < 0) { + LOG(FATAL) << "Congratulations, you found a bug in Ceres." + << "Please report it. dormr fatal error." + << "Argument: " << -info << " is invalid."; + } else if (info > 0) { + *message = + "QR factorization failure. The factorization is not full rank. R has " + "zeros on the diagonal."; + termination_type_ = LINEAR_SOLVER_FAILURE; + } else { + std::copy_n(q_transpose_rhs_.data(), num_cols_, solution); + termination_type_ = LINEAR_SOLVER_SUCCESS; + } + + return termination_type_; +} + +#endif // CERES_NO_LAPACK + +#ifndef CERES_NO_CUDA + +bool CUDADenseQR::Init(ContextImpl* context, std::string* message) { + if (!context->InitCUDA(message)) { + return false; + } + cublas_handle_ = context->cublas_handle_; + cusolver_handle_ = context->cusolver_handle_; + stream_ = context->stream_; + error_.Reserve(1); + *message = "CUDADenseQR::Init Success."; + return true; +} + +LinearSolverTerminationType CUDADenseQR::Factorize(int num_rows, + int num_cols, + double* lhs, + std::string* message) { + factorize_result_ = LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + lhs_.Reserve(num_rows * num_cols); + tau_.Reserve(std::min(num_rows, num_cols)); + num_rows_ = num_rows; + num_cols_ = num_cols; + lhs_.CopyToGpuAsync(lhs, num_rows * num_cols, stream_); + int device_workspace_size = 0; + if (cusolverDnDgeqrf_bufferSize(cusolver_handle_, + num_rows, + num_cols, + lhs_.data(), + num_rows, + &device_workspace_size) != + CUSOLVER_STATUS_SUCCESS) { + *message = "cuSolverDN::cusolverDnDgeqrf_bufferSize failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + device_workspace_.Reserve(device_workspace_size); + if (cusolverDnDgeqrf(cusolver_handle_, + num_rows, + num_cols, + lhs_.data(), + num_rows, + tau_.data(), + reinterpret_cast<double*>(device_workspace_.data()), + device_workspace_.size(), + error_.data()) != CUSOLVER_STATUS_SUCCESS) { + *message = "cuSolverDN::cusolverDnDgeqrf failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + if (cudaDeviceSynchronize() != cudaSuccess || + cudaStreamSynchronize(stream_) != cudaSuccess) { + *message = "Cuda device synchronization failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + int error = 0; + error_.CopyToHost(&error, 1); + if (error < 0) { + LOG(FATAL) << "Congratulations, you found a bug in Ceres - " + << "please report it. " + << "cuSolverDN::cusolverDnDgeqrf fatal error. " + << "Argument: " << -error << " is invalid."; + // The following line is unreachable, but return failure just to be + // pedantic, since the compiler does not know that. + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + + *message = "Success"; + factorize_result_ = LinearSolverTerminationType::LINEAR_SOLVER_SUCCESS; + return LinearSolverTerminationType::LINEAR_SOLVER_SUCCESS; +} + +LinearSolverTerminationType CUDADenseQR::Solve(const double* rhs, + double* solution, + std::string* message) { + if (factorize_result_ != LinearSolverTerminationType::LINEAR_SOLVER_SUCCESS) { + *message = "Factorize did not complete succesfully previously."; + return factorize_result_; + } + rhs_.CopyToGpuAsync(rhs, num_rows_, stream_); + int device_workspace_size = 0; + if (cusolverDnDormqr_bufferSize(cusolver_handle_, + CUBLAS_SIDE_LEFT, + CUBLAS_OP_T, + num_rows_, + 1, + num_cols_, + lhs_.data(), + num_rows_, + tau_.data(), + rhs_.data(), + num_rows_, + &device_workspace_size) != + CUSOLVER_STATUS_SUCCESS) { + *message = "cuSolverDN::cusolverDnDormqr_bufferSize failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + device_workspace_.Reserve(device_workspace_size); + // Compute rhs = Q^T * rhs, assuming that lhs has already been factorized. + // The result of factorization would have stored Q in a packed form in lhs_. + if (cusolverDnDormqr(cusolver_handle_, + CUBLAS_SIDE_LEFT, + CUBLAS_OP_T, + num_rows_, + 1, + num_cols_, + lhs_.data(), + num_rows_, + tau_.data(), + rhs_.data(), + num_rows_, + reinterpret_cast<double*>(device_workspace_.data()), + device_workspace_.size(), + error_.data()) != CUSOLVER_STATUS_SUCCESS) { + *message = "cuSolverDN::cusolverDnDormqr failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + int error = 0; + error_.CopyToHost(&error, 1); + if (error < 0) { + LOG(FATAL) << "Congratulations, you found a bug in Ceres. " + << "Please report it." + << "cuSolverDN::cusolverDnDormqr fatal error. " + << "Argument: " << -error << " is invalid."; + } + // Compute the solution vector as x = R \ (Q^T * rhs). Since the previous step + // replaced rhs by (Q^T * rhs), this is just x = R \ rhs. + if (cublasDtrsv(cublas_handle_, + CUBLAS_FILL_MODE_UPPER, + CUBLAS_OP_N, + CUBLAS_DIAG_NON_UNIT, + num_cols_, + lhs_.data(), + num_rows_, + rhs_.data(), + 1) != CUBLAS_STATUS_SUCCESS) { + *message = "cuBLAS::cublasDtrsv failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + if (cudaDeviceSynchronize() != cudaSuccess || + cudaStreamSynchronize(stream_) != cudaSuccess) { + *message = "Cuda device synchronization failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + rhs_.CopyToHost(solution, num_cols_); + *message = "Success"; + return LinearSolverTerminationType::LINEAR_SOLVER_SUCCESS; +} + +std::unique_ptr<CUDADenseQR> CUDADenseQR::Create( + const LinearSolver::Options& options) { + if (options.dense_linear_algebra_library_type != CUDA) { + // The user called the wrong factory method. + return nullptr; + } + auto cuda_dense_qr = std::unique_ptr<CUDADenseQR>(new CUDADenseQR()); + std::string cuda_error; + if (cuda_dense_qr->Init(options.context, &cuda_error)) { + return cuda_dense_qr; + } + // Initialization failed, destroy the object (done automatically) and return a + // nullptr. + LOG(ERROR) << "CUDADenseQR::Init failed: " << cuda_error; + return nullptr; +} + +CUDADenseQR::CUDADenseQR() = default; + +#endif // CERES_NO_CUDA + +} // namespace internal +} // namespace ceres diff --git a/extern/ceres/internal/ceres/dense_qr.h b/extern/ceres/internal/ceres/dense_qr.h new file mode 100644 index 00000000000..7a2ffb52ae6 --- /dev/null +++ b/extern/ceres/internal/ceres/dense_qr.h @@ -0,0 +1,207 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#ifndef CERES_INTERNAL_DENSE_QR_H_ +#define CERES_INTERNAL_DENSE_QR_H_ + +// This include must come before any #ifndef check on Ceres compile options. +// clang-format off +#include "ceres/internal/config.h" +// clang-format on + +#include <memory> +#include <vector> + +#include "Eigen/Dense" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" +#include "ceres/linear_solver.h" +#include "glog/logging.h" + +#ifndef CERES_NO_CUDA +#include "ceres/context_impl.h" +#include "ceres/cuda_buffer.h" +#include "cublas_v2.h" +#include "cuda_runtime.h" +#include "cusolverDn.h" +#endif // CERES_NO_CUDA + +namespace ceres { +namespace internal { + +// An interface that abstracts away the internal details of various dense linear +// algebra libraries and offers a simple API for solving dense linear systems +// using a QR factorization. +class CERES_NO_EXPORT DenseQR { + public: + static std::unique_ptr<DenseQR> Create(const LinearSolver::Options& options); + + virtual ~DenseQR(); + + // Computes the QR factorization of the given matrix. + // + // The input matrix lhs is assumed to be a column-major num_rows x num_cols + // matrix. + // + // The input matrix lhs may be modified by the implementation to store the + // factorization, irrespective of whether the factorization succeeds or not. + // As a result it is the user's responsibility to ensure that lhs is valid + // when Solve is called. + virtual LinearSolverTerminationType Factorize(int num_rows, + int num_cols, + double* lhs, + std::string* message) = 0; + + // Computes the solution to the equation + // + // lhs * solution = rhs + // + // Calling Solve without calling Factorize is undefined behaviour. It is the + // user's responsibility to ensure that the input matrix lhs passed to + // Factorize has not been freed/modified when Solve is called. + virtual LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) = 0; + + // Convenience method which combines a call to Factorize and Solve. Solve is + // only called if Factorize returns LINEAR_SOLVER_SUCCESS. + // + // The input matrix lhs may be modified by the implementation to store the + // factorization, irrespective of whether the method succeeds or not. It is + // the user's responsibility to ensure that lhs is valid if and when Solve is + // called again after this call. + LinearSolverTerminationType FactorAndSolve(int num_rows, + int num_cols, + double* lhs, + const double* rhs, + double* solution, + std::string* message); +}; + +class CERES_NO_EXPORT EigenDenseQR final : public DenseQR { + public: + LinearSolverTerminationType Factorize(int num_rows, + int num_cols, + double* lhs, + std::string* message) override; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override; + + private: + using QRType = Eigen::HouseholderQR<Eigen::Ref<ColMajorMatrix>>; + std::unique_ptr<QRType> qr_; +}; + +#ifndef CERES_NO_LAPACK +class CERES_NO_EXPORT LAPACKDenseQR final : public DenseQR { + public: + LinearSolverTerminationType Factorize(int num_rows, + int num_cols, + double* lhs, + std::string* message) override; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override; + + private: + double* lhs_ = nullptr; + int num_rows_; + int num_cols_; + LinearSolverTerminationType termination_type_ = LINEAR_SOLVER_FATAL_ERROR; + Vector work_; + Vector tau_; + Vector q_transpose_rhs_; +}; +#endif // CERES_NO_LAPACK + +#ifndef CERES_NO_CUDA +// Implementation of DenseQR using the 32-bit cuSolverDn interface. A +// requirement for using this solver is that the lhs must not be rank deficient. +// This is because cuSolverDn does not implement the singularity-checking +// wrapper trtrs, hence this solver directly uses trsv from CUBLAS for the +// backsubstitution. +class CERES_NO_EXPORT CUDADenseQR final : public DenseQR { + public: + static std::unique_ptr<CUDADenseQR> Create( + const LinearSolver::Options& options); + CUDADenseQR(const CUDADenseQR&) = delete; + CUDADenseQR& operator=(const CUDADenseQR&) = delete; + LinearSolverTerminationType Factorize(int num_rows, + int num_cols, + double* lhs, + std::string* message) override; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override; + + private: + CUDADenseQR(); + // Picks up the cuSolverDN, cuBLAS, and cuStream handles from the context. If + // the context is unable to initialize CUDA, returns false with a + // human-readable message indicating the reason. + bool Init(ContextImpl* context, std::string* message); + + // Handle to the cuSOLVER context. + cusolverDnHandle_t cusolver_handle_ = nullptr; + // Handle to cuBLAS context. + cublasHandle_t cublas_handle_ = nullptr; + // CUDA device stream. + cudaStream_t stream_ = nullptr; + // Number of rowns in the A matrix, to be cached between calls to *Factorize + // and *Solve. + size_t num_rows_ = 0; + // Number of columns in the A matrix, to be cached between calls to *Factorize + // and *Solve. + size_t num_cols_ = 0; + // GPU memory allocated for the A matrix (lhs matrix). + CudaBuffer<double> lhs_; + // GPU memory allocated for the B matrix (rhs vector). + CudaBuffer<double> rhs_; + // GPU memory allocated for the TAU matrix (scaling of householder vectors). + CudaBuffer<double> tau_; + // Scratch space for cuSOLVER on the GPU. + CudaBuffer<double> device_workspace_; + // Required for error handling with cuSOLVER. + CudaBuffer<int> error_; + // Cache the result of Factorize to ensure that when Solve is called, the + // factiorization of lhs is valid. + LinearSolverTerminationType factorize_result_ = LINEAR_SOLVER_FATAL_ERROR; +}; + +#endif // CERES_NO_CUDA + +} // namespace internal +} // namespace ceres + +#include "ceres/internal/reenable_warnings.h" + +#endif // CERES_INTERNAL_DENSE_QR_H_ diff --git a/extern/ceres/internal/ceres/dense_qr_solver.cc b/extern/ceres/internal/ceres/dense_qr_solver.cc index 44388f30aee..24cb25abd8e 100644 --- a/extern/ceres/internal/ceres/dense_qr_solver.cc +++ b/extern/ceres/internal/ceres/dense_qr_solver.cc @@ -33,9 +33,9 @@ #include <cstddef> #include "Eigen/Dense" +#include "ceres/dense_qr.h" #include "ceres/dense_sparse_matrix.h" #include "ceres/internal/eigen.h" -#include "ceres/lapack.h" #include "ceres/linear_solver.h" #include "ceres/types.h" #include "ceres/wall_time.h" @@ -44,124 +44,40 @@ namespace ceres { namespace internal { DenseQRSolver::DenseQRSolver(const LinearSolver::Options& options) - : options_(options) { - work_.resize(1); -} + : options_(options), dense_qr_(DenseQR::Create(options)) {} LinearSolver::Summary DenseQRSolver::SolveImpl( DenseSparseMatrix* A, const double* b, const LinearSolver::PerSolveOptions& per_solve_options, double* x) { - if (options_.dense_linear_algebra_library_type == EIGEN) { - return SolveUsingEigen(A, b, per_solve_options, x); - } else { - return SolveUsingLAPACK(A, b, per_solve_options, x); - } -} - -LinearSolver::Summary DenseQRSolver::SolveUsingLAPACK( - DenseSparseMatrix* A, - const double* b, - const LinearSolver::PerSolveOptions& per_solve_options, - double* x) { EventLogger event_logger("DenseQRSolver::Solve"); const int num_rows = A->num_rows(); const int num_cols = A->num_cols(); + const int num_augmented_rows = + num_rows + ((per_solve_options.D != nullptr) ? num_cols : 0); - if (per_solve_options.D != NULL) { - // Temporarily append a diagonal block to the A matrix, but undo - // it before returning the matrix to the user. - A->AppendDiagonal(per_solve_options.D); + if (lhs_.rows() != num_augmented_rows || lhs_.cols() != num_cols) { + lhs_.resize(num_augmented_rows, num_cols); + rhs_.resize(num_augmented_rows); } - // TODO(sameeragarwal): Since we are copying anyways, the diagonal - // can be appended to the matrix instead of doing it on A. - lhs_ = A->matrix(); - - if (per_solve_options.D != NULL) { - // Undo the modifications to the matrix A. - A->RemoveDiagonal(); - } - - // rhs = [b;0] to account for the additional rows in the lhs. - if (rhs_.rows() != lhs_.rows()) { - rhs_.resize(lhs_.rows()); - } - rhs_.setZero(); + lhs_.topRows(num_rows) = A->matrix(); rhs_.head(num_rows) = ConstVectorRef(b, num_rows); - if (work_.rows() == 1) { - const int work_size = - LAPACK::EstimateWorkSizeForQR(lhs_.rows(), lhs_.cols()); - VLOG(3) << "Working memory for Dense QR factorization: " - << work_size * sizeof(double); - work_.resize(work_size); + if (num_rows != num_augmented_rows) { + lhs_.bottomRows(num_cols) = + ConstVectorRef(per_solve_options.D, num_cols).asDiagonal(); + rhs_.tail(num_cols).setZero(); } LinearSolver::Summary summary; + summary.termination_type = dense_qr_->FactorAndSolve( + lhs_.rows(), lhs_.cols(), lhs_.data(), rhs_.data(), x, &summary.message); summary.num_iterations = 1; - summary.termination_type = LAPACK::SolveInPlaceUsingQR(lhs_.rows(), - lhs_.cols(), - lhs_.data(), - work_.rows(), - work_.data(), - rhs_.data(), - &summary.message); event_logger.AddEvent("Solve"); - if (summary.termination_type == LINEAR_SOLVER_SUCCESS) { - VectorRef(x, num_cols) = rhs_.head(num_cols); - } - - event_logger.AddEvent("TearDown"); - return summary; -} - -LinearSolver::Summary DenseQRSolver::SolveUsingEigen( - DenseSparseMatrix* A, - const double* b, - const LinearSolver::PerSolveOptions& per_solve_options, - double* x) { - EventLogger event_logger("DenseQRSolver::Solve"); - - const int num_rows = A->num_rows(); - const int num_cols = A->num_cols(); - - if (per_solve_options.D != NULL) { - // Temporarily append a diagonal block to the A matrix, but undo - // it before returning the matrix to the user. - A->AppendDiagonal(per_solve_options.D); - } - - // rhs = [b;0] to account for the additional rows in the lhs. - const int augmented_num_rows = - num_rows + ((per_solve_options.D != NULL) ? num_cols : 0); - if (rhs_.rows() != augmented_num_rows) { - rhs_.resize(augmented_num_rows); - rhs_.setZero(); - } - rhs_.head(num_rows) = ConstVectorRef(b, num_rows); - event_logger.AddEvent("Setup"); - - // Solve the system. - VectorRef(x, num_cols) = A->matrix().householderQr().solve(rhs_); - event_logger.AddEvent("Solve"); - - if (per_solve_options.D != NULL) { - // Undo the modifications to the matrix A. - A->RemoveDiagonal(); - } - - // We always succeed, since the QR solver returns the best solution - // it can. It is the job of the caller to determine if the solution - // is good enough or not. - LinearSolver::Summary summary; - summary.num_iterations = 1; - summary.termination_type = LINEAR_SOLVER_SUCCESS; - summary.message = "Success."; - event_logger.AddEvent("TearDown"); return summary; } diff --git a/extern/ceres/internal/ceres/dense_qr_solver.h b/extern/ceres/internal/ceres/dense_qr_solver.h index 980243bd6c6..39922a2692b 100644 --- a/extern/ceres/internal/ceres/dense_qr_solver.h +++ b/extern/ceres/internal/ceres/dense_qr_solver.h @@ -32,8 +32,12 @@ #ifndef CERES_INTERNAL_DENSE_QR_SOLVER_H_ #define CERES_INTERNAL_DENSE_QR_SOLVER_H_ +#include <memory> + +#include "ceres/dense_qr.h" +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { @@ -79,7 +83,7 @@ class DenseSparseMatrix; // library. This solver always returns a solution, it is the user's // responsibility to judge if the solution is good enough for their // purposes. -class CERES_EXPORT_INTERNAL DenseQRSolver : public DenseSparseMatrixSolver { +class CERES_NO_EXPORT DenseQRSolver final : public DenseSparseMatrixSolver { public: explicit DenseQRSolver(const LinearSolver::Options& options); @@ -105,10 +109,12 @@ class CERES_EXPORT_INTERNAL DenseQRSolver : public DenseSparseMatrixSolver { const LinearSolver::Options options_; ColMajorMatrix lhs_; Vector rhs_; - Vector work_; + std::unique_ptr<DenseQR> dense_qr_; }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_DENSE_QR_SOLVER_H_ diff --git a/extern/ceres/internal/ceres/dense_sparse_matrix.cc b/extern/ceres/internal/ceres/dense_sparse_matrix.cc index 53207fe300e..8b967f2ade7 100644 --- a/extern/ceres/internal/ceres/dense_sparse_matrix.cc +++ b/extern/ceres/internal/ceres/dense_sparse_matrix.cc @@ -31,9 +31,10 @@ #include "ceres/dense_sparse_matrix.h" #include <algorithm> +#include <utility> #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/triplet_sparse_matrix.h" #include "glog/logging.h" @@ -41,28 +42,10 @@ namespace ceres { namespace internal { DenseSparseMatrix::DenseSparseMatrix(int num_rows, int num_cols) - : has_diagonal_appended_(false), has_diagonal_reserved_(false) { - m_.resize(num_rows, num_cols); - m_.setZero(); -} - -DenseSparseMatrix::DenseSparseMatrix(int num_rows, - int num_cols, - bool reserve_diagonal) - : has_diagonal_appended_(false), has_diagonal_reserved_(reserve_diagonal) { - if (reserve_diagonal) { - // Allocate enough space for the diagonal. - m_.resize(num_rows + num_cols, num_cols); - } else { - m_.resize(num_rows, num_cols); - } - m_.setZero(); -} + : m_(Matrix(num_rows, num_cols)) {} DenseSparseMatrix::DenseSparseMatrix(const TripletSparseMatrix& m) - : m_(Eigen::MatrixXd::Zero(m.num_rows(), m.num_cols())), - has_diagonal_appended_(false), - has_diagonal_reserved_(false) { + : m_(Matrix::Zero(m.num_rows(), m.num_cols())) { const double* values = m.values(); const int* rows = m.rows(); const int* cols = m.cols(); @@ -73,8 +56,7 @@ DenseSparseMatrix::DenseSparseMatrix(const TripletSparseMatrix& m) } } -DenseSparseMatrix::DenseSparseMatrix(const ColMajorMatrix& m) - : m_(m), has_diagonal_appended_(false), has_diagonal_reserved_(false) {} +DenseSparseMatrix::DenseSparseMatrix(Matrix m) : m_(std::move(m)) {} void DenseSparseMatrix::SetZero() { m_.setZero(); } @@ -96,72 +78,22 @@ void DenseSparseMatrix::ScaleColumns(const double* scale) { } void DenseSparseMatrix::ToDenseMatrix(Matrix* dense_matrix) const { - *dense_matrix = m_.block(0, 0, num_rows(), num_cols()); + *dense_matrix = m_; } -void DenseSparseMatrix::AppendDiagonal(double* d) { - CHECK(!has_diagonal_appended_); - if (!has_diagonal_reserved_) { - ColMajorMatrix tmp = m_; - m_.resize(m_.rows() + m_.cols(), m_.cols()); - m_.setZero(); - m_.block(0, 0, tmp.rows(), tmp.cols()) = tmp; - has_diagonal_reserved_ = true; - } - - m_.bottomLeftCorner(m_.cols(), m_.cols()) = - ConstVectorRef(d, m_.cols()).asDiagonal(); - has_diagonal_appended_ = true; -} - -void DenseSparseMatrix::RemoveDiagonal() { - CHECK(has_diagonal_appended_); - has_diagonal_appended_ = false; - // Leave the diagonal reserved. -} - -int DenseSparseMatrix::num_rows() const { - if (has_diagonal_reserved_ && !has_diagonal_appended_) { - return m_.rows() - m_.cols(); - } - return m_.rows(); -} +int DenseSparseMatrix::num_rows() const { return m_.rows(); } int DenseSparseMatrix::num_cols() const { return m_.cols(); } -int DenseSparseMatrix::num_nonzeros() const { - if (has_diagonal_reserved_ && !has_diagonal_appended_) { - return (m_.rows() - m_.cols()) * m_.cols(); - } - return m_.rows() * m_.cols(); -} +int DenseSparseMatrix::num_nonzeros() const { return m_.rows() * m_.cols(); } -ConstColMajorMatrixRef DenseSparseMatrix::matrix() const { - return ConstColMajorMatrixRef( - m_.data(), - ((has_diagonal_reserved_ && !has_diagonal_appended_) - ? m_.rows() - m_.cols() - : m_.rows()), - m_.cols(), - Eigen::Stride<Eigen::Dynamic, 1>(m_.rows(), 1)); -} +const Matrix& DenseSparseMatrix::matrix() const { return m_; } -ColMajorMatrixRef DenseSparseMatrix::mutable_matrix() { - return ColMajorMatrixRef(m_.data(), - ((has_diagonal_reserved_ && !has_diagonal_appended_) - ? m_.rows() - m_.cols() - : m_.rows()), - m_.cols(), - Eigen::Stride<Eigen::Dynamic, 1>(m_.rows(), 1)); -} +Matrix* DenseSparseMatrix::mutable_matrix() { return &m_; } void DenseSparseMatrix::ToTextFile(FILE* file) const { CHECK(file != nullptr); - const int active_rows = (has_diagonal_reserved_ && !has_diagonal_appended_) - ? (m_.rows() - m_.cols()) - : m_.rows(); - - for (int r = 0; r < active_rows; ++r) { + for (int r = 0; r < m_.rows(); ++r) { for (int c = 0; c < m_.cols(); ++c) { fprintf(file, "% 10d % 10d %17f\n", r, c, m_(r, c)); } diff --git a/extern/ceres/internal/ceres/dense_sparse_matrix.h b/extern/ceres/internal/ceres/dense_sparse_matrix.h index 94064b3eddc..655cbb8a3db 100644 --- a/extern/ceres/internal/ceres/dense_sparse_matrix.h +++ b/extern/ceres/internal/ceres/dense_sparse_matrix.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -33,8 +33,9 @@ #ifndef CERES_INTERNAL_DENSE_SPARSE_MATRIX_H_ #define CERES_INTERNAL_DENSE_SPARSE_MATRIX_H_ +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/sparse_matrix.h" #include "ceres/types.h" @@ -43,17 +44,13 @@ namespace internal { class TripletSparseMatrix; -class CERES_EXPORT_INTERNAL DenseSparseMatrix : public SparseMatrix { +class CERES_NO_EXPORT DenseSparseMatrix final : public SparseMatrix { public: // Build a matrix with the same content as the TripletSparseMatrix // m. This assumes that m does not have any repeated entries. explicit DenseSparseMatrix(const TripletSparseMatrix& m); - explicit DenseSparseMatrix(const ColMajorMatrix& m); - + explicit DenseSparseMatrix(Matrix m); DenseSparseMatrix(int num_rows, int num_cols); - DenseSparseMatrix(int num_rows, int num_cols, bool reserve_diagonal); - - virtual ~DenseSparseMatrix() {} // SparseMatrix interface. void SetZero() final; @@ -69,40 +66,16 @@ class CERES_EXPORT_INTERNAL DenseSparseMatrix : public SparseMatrix { const double* values() const final { return m_.data(); } double* mutable_values() final { return m_.data(); } - ConstColMajorMatrixRef matrix() const; - ColMajorMatrixRef mutable_matrix(); - - // Only one diagonal can be appended at a time. The diagonal is appended to - // as a new set of rows, e.g. - // - // Original matrix: - // - // x x x - // x x x - // x x x - // - // After append diagonal (1, 2, 3): - // - // x x x - // x x x - // x x x - // 1 0 0 - // 0 2 0 - // 0 0 3 - // - // Calling RemoveDiagonal removes the block. It is a fatal error to append a - // diagonal to a matrix that already has an appended diagonal, and it is also - // a fatal error to remove a diagonal from a matrix that has none. - void AppendDiagonal(double* d); - void RemoveDiagonal(); + const Matrix& matrix() const; + Matrix* mutable_matrix(); private: - ColMajorMatrix m_; - bool has_diagonal_appended_; - bool has_diagonal_reserved_; + Matrix m_; }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_DENSE_SPARSE_MATRIX_H_ diff --git a/extern/ceres/internal/ceres/detect_structure.h b/extern/ceres/internal/ceres/detect_structure.h index 06242307ca8..6151c042256 100644 --- a/extern/ceres/internal/ceres/detect_structure.h +++ b/extern/ceres/internal/ceres/detect_structure.h @@ -32,7 +32,8 @@ #define CERES_INTERNAL_DETECT_STRUCTURE_H_ #include "ceres/block_structure.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -56,13 +57,15 @@ namespace internal { // Note: The structure of rows without any e-blocks has no effect on // the values returned by this function. It is entirely possible that // the f_block_size and row_blocks_size is not constant in such rows. -void CERES_EXPORT DetectStructure(const CompressedRowBlockStructure& bs, - const int num_eliminate_blocks, - int* row_block_size, - int* e_block_size, - int* f_block_size); +void CERES_NO_EXPORT DetectStructure(const CompressedRowBlockStructure& bs, + const int num_eliminate_blocks, + int* row_block_size, + int* e_block_size, + int* f_block_size); } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_DETECT_STRUCTURE_H_ diff --git a/extern/ceres/internal/ceres/dogleg_strategy.cc b/extern/ceres/internal/ceres/dogleg_strategy.cc index 03ae22f7e57..65f7ccd8480 100644 --- a/extern/ceres/internal/ceres/dogleg_strategy.cc +++ b/extern/ceres/internal/ceres/dogleg_strategy.cc @@ -480,7 +480,7 @@ bool DoglegStrategy::FindMinimumOnTrustRegionBoundary(Vector2d* minimum) const { // Find the real parts y_i of its roots (not only the real roots). Vector roots_real; - if (!FindPolynomialRoots(polynomial, &roots_real, NULL)) { + if (!FindPolynomialRoots(polynomial, &roots_real, nullptr)) { // Failed to find the roots of the polynomial, i.e. the candidate // solutions of the constrained problem. Report this back to the caller. return false; diff --git a/extern/ceres/internal/ceres/dogleg_strategy.h b/extern/ceres/internal/ceres/dogleg_strategy.h index cc3778ea2a0..1d219afe8bc 100644 --- a/extern/ceres/internal/ceres/dogleg_strategy.h +++ b/extern/ceres/internal/ceres/dogleg_strategy.h @@ -31,7 +31,8 @@ #ifndef CERES_INTERNAL_DOGLEG_STRATEGY_H_ #define CERES_INTERNAL_DOGLEG_STRATEGY_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" #include "ceres/trust_region_strategy.h" @@ -53,10 +54,9 @@ namespace internal { // DoglegStrategy follows the approach by Shultz, Schnabel, Byrd. // This finds the exact optimum over the two-dimensional subspace // spanned by the two Dogleg vectors. -class CERES_EXPORT_INTERNAL DoglegStrategy : public TrustRegionStrategy { +class CERES_NO_EXPORT DoglegStrategy final : public TrustRegionStrategy { public: explicit DoglegStrategy(const TrustRegionStrategy::Options& options); - virtual ~DoglegStrategy() {} // TrustRegionStrategy interface Summary ComputeStep(const PerSolveOptions& per_solve_options, @@ -65,7 +65,7 @@ class CERES_EXPORT_INTERNAL DoglegStrategy : public TrustRegionStrategy { double* step) final; void StepAccepted(double step_quality) final; void StepRejected(double step_quality) final; - void StepIsInvalid(); + void StepIsInvalid() override; double Radius() const final; // These functions are predominantly for testing. @@ -76,8 +76,8 @@ class CERES_EXPORT_INTERNAL DoglegStrategy : public TrustRegionStrategy { Matrix subspace_B() const { return subspace_B_; } private: - typedef Eigen::Matrix<double, 2, 1, Eigen::DontAlign> Vector2d; - typedef Eigen::Matrix<double, 2, 2, Eigen::DontAlign> Matrix2d; + using Vector2d = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>; + using Matrix2d = Eigen::Matrix<double, 2, 2, Eigen::DontAlign>; LinearSolver::Summary ComputeGaussNewtonStep( const PerSolveOptions& per_solve_options, @@ -162,4 +162,6 @@ class CERES_EXPORT_INTERNAL DoglegStrategy : public TrustRegionStrategy { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_DOGLEG_STRATEGY_H_ diff --git a/extern/ceres/internal/ceres/dynamic_compressed_row_finalizer.h b/extern/ceres/internal/ceres/dynamic_compressed_row_finalizer.h index 30c98d86b6f..fedee3b7a83 100644 --- a/extern/ceres/internal/ceres/dynamic_compressed_row_finalizer.h +++ b/extern/ceres/internal/ceres/dynamic_compressed_row_finalizer.h @@ -33,13 +33,14 @@ #include "ceres/casts.h" #include "ceres/dynamic_compressed_row_sparse_matrix.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { -struct DynamicCompressedRowJacobianFinalizer { +struct CERES_NO_EXPORT DynamicCompressedRowJacobianFinalizer { void operator()(SparseMatrix* base_jacobian, int num_parameters) { - DynamicCompressedRowSparseMatrix* jacobian = + auto* jacobian = down_cast<DynamicCompressedRowSparseMatrix*>(base_jacobian); jacobian->Finalize(num_parameters); } diff --git a/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.cc b/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.cc index 1749449043e..8c254e98f46 100644 --- a/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.cc +++ b/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -30,6 +30,8 @@ #include "ceres/dynamic_compressed_row_jacobian_writer.h" +#include <memory> + #include "ceres/casts.h" #include "ceres/compressed_row_jacobian_writer.h" #include "ceres/dynamic_compressed_row_sparse_matrix.h" @@ -43,25 +45,24 @@ namespace internal { using std::pair; using std::vector; -ScratchEvaluatePreparer* +std::unique_ptr<ScratchEvaluatePreparer[]> DynamicCompressedRowJacobianWriter::CreateEvaluatePreparers(int num_threads) { return ScratchEvaluatePreparer::Create(*program_, num_threads); } -SparseMatrix* DynamicCompressedRowJacobianWriter::CreateJacobian() const { - DynamicCompressedRowSparseMatrix* jacobian = - new DynamicCompressedRowSparseMatrix(program_->NumResiduals(), - program_->NumEffectiveParameters(), - 0 /* max_num_nonzeros */); - return jacobian; +std::unique_ptr<SparseMatrix> +DynamicCompressedRowJacobianWriter::CreateJacobian() const { + return std::make_unique<DynamicCompressedRowSparseMatrix>( + program_->NumResiduals(), + program_->NumEffectiveParameters(), + 0 /* max_num_nonzeros */); } void DynamicCompressedRowJacobianWriter::Write(int residual_id, int residual_offset, double** jacobians, SparseMatrix* base_jacobian) { - DynamicCompressedRowSparseMatrix* jacobian = - down_cast<DynamicCompressedRowSparseMatrix*>(base_jacobian); + auto* jacobian = down_cast<DynamicCompressedRowSparseMatrix*>(base_jacobian); // Get the `residual_block` of interest. const ResidualBlock* residual_block = @@ -77,12 +78,11 @@ void DynamicCompressedRowJacobianWriter::Write(int residual_id, jacobian->ClearRows(residual_offset, num_residuals); // Iterate over each parameter block. - for (int i = 0; i < evaluated_jacobian_blocks.size(); ++i) { + for (const auto& evaluated_jacobian_block : evaluated_jacobian_blocks) { const ParameterBlock* parameter_block = - program_->parameter_blocks()[evaluated_jacobian_blocks[i].first]; - const int parameter_block_jacobian_index = - evaluated_jacobian_blocks[i].second; - const int parameter_block_size = parameter_block->LocalSize(); + program_->parameter_blocks()[evaluated_jacobian_block.first]; + const int parameter_block_jacobian_index = evaluated_jacobian_block.second; + const int parameter_block_size = parameter_block->TangentSize(); const double* parameter_jacobian = jacobians[parameter_block_jacobian_index]; diff --git a/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.h b/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.h index ef8fa25d7d4..794a9b4c1e6 100644 --- a/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.h +++ b/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.h @@ -34,7 +34,10 @@ #ifndef CERES_INTERNAL_DYNAMIC_COMPRESSED_ROW_JACOBIAN_WRITER_H_ #define CERES_INTERNAL_DYNAMIC_COMPRESSED_ROW_JACOBIAN_WRITER_H_ +#include <memory> + #include "ceres/evaluator.h" +#include "ceres/internal/export.h" #include "ceres/scratch_evaluate_preparer.h" namespace ceres { @@ -43,7 +46,7 @@ namespace internal { class Program; class SparseMatrix; -class DynamicCompressedRowJacobianWriter { +class CERES_NO_EXPORT DynamicCompressedRowJacobianWriter { public: DynamicCompressedRowJacobianWriter(Evaluator::Options /* ignored */, Program* program) @@ -55,12 +58,13 @@ class DynamicCompressedRowJacobianWriter { // the cost functions. The scratch space is therefore used to store // the jacobians (including zeros) temporarily before only the non-zero // entries are copied over to the larger jacobian in `Write`. - ScratchEvaluatePreparer* CreateEvaluatePreparers(int num_threads); + std::unique_ptr<ScratchEvaluatePreparer[]> CreateEvaluatePreparers( + int num_threads); // Return a `DynamicCompressedRowSparseMatrix` which is filled by // `Write`. Note that `Finalize` must be called to make the // `CompressedRowSparseMatrix` interface valid. - SparseMatrix* CreateJacobian() const; + std::unique_ptr<SparseMatrix> CreateJacobian() const; // Write only the non-zero jacobian entries for a residual block // (specified by `residual_id`) into `base_jacobian`, starting at the row diff --git a/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.cc b/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.cc index 936e682b763..7185e14a411 100644 --- a/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.cc +++ b/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -70,8 +70,8 @@ void DynamicCompressedRowSparseMatrix::Finalize(int num_additional_elements) { // Count the number of non-zeros and resize `cols_` and `values_`. int num_jacobian_nonzeros = 0; - for (int i = 0; i < dynamic_cols_.size(); ++i) { - num_jacobian_nonzeros += dynamic_cols_[i].size(); + for (const auto& dynamic_col : dynamic_cols_) { + num_jacobian_nonzeros += dynamic_col.size(); } SetMaxNumNonZeros(num_jacobian_nonzeros + num_additional_elements); diff --git a/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.h b/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.h index d06c36ebb94..5b4c402f830 100644 --- a/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.h +++ b/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.h @@ -44,12 +44,13 @@ #include <vector> #include "ceres/compressed_row_sparse_matrix.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { -class CERES_EXPORT_INTERNAL DynamicCompressedRowSparseMatrix +class CERES_NO_EXPORT DynamicCompressedRowSparseMatrix final : public CompressedRowSparseMatrix { public: // Set the number of rows and columns for the underlyig @@ -100,4 +101,6 @@ class CERES_EXPORT_INTERNAL DynamicCompressedRowSparseMatrix } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_DYNAMIC_COMPRESSED_ROW_SPARSE_MATRIX_H_ diff --git a/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc b/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc index d31c4228f05..5e907e18d51 100644 --- a/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc +++ b/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc @@ -35,10 +35,12 @@ #include <ctime> #include <memory> #include <sstream> +#include <utility> #include "Eigen/SparseCore" #include "ceres/compressed_row_sparse_matrix.h" #include "ceres/cxsparse.h" +#include "ceres/internal/config.h" #include "ceres/internal/eigen.h" #include "ceres/linear_solver.h" #include "ceres/suitesparse.h" @@ -54,8 +56,8 @@ namespace ceres { namespace internal { DynamicSparseNormalCholeskySolver::DynamicSparseNormalCholeskySolver( - const LinearSolver::Options& options) - : options_(options) {} + LinearSolver::Options options) + : options_(std::move(options)) {} LinearSolver::Summary DynamicSparseNormalCholeskySolver::SolveImpl( CompressedRowSparseMatrix* A, @@ -71,11 +73,11 @@ LinearSolver::Summary DynamicSparseNormalCholeskySolver::SolveImpl( // it before returning the matrix to the user. std::unique_ptr<CompressedRowSparseMatrix> regularizer; if (!A->col_blocks().empty()) { - regularizer.reset(CompressedRowSparseMatrix::CreateBlockDiagonalMatrix( - per_solve_options.D, A->col_blocks())); + regularizer = CompressedRowSparseMatrix::CreateBlockDiagonalMatrix( + per_solve_options.D, A->col_blocks()); } else { - regularizer.reset( - new CompressedRowSparseMatrix(per_solve_options.D, num_cols)); + regularizer = std::make_unique<CompressedRowSparseMatrix>( + per_solve_options.D, num_cols); } A->AppendRows(*regularizer); } @@ -123,12 +125,13 @@ LinearSolver::Summary DynamicSparseNormalCholeskySolver::SolveImplUsingEigen( EventLogger event_logger("DynamicSparseNormalCholeskySolver::Eigen::Solve"); - Eigen::MappedSparseMatrix<double, Eigen::RowMajor> a(A->num_rows(), - A->num_cols(), - A->num_nonzeros(), - A->mutable_rows(), - A->mutable_cols(), - A->mutable_values()); + Eigen::Map<Eigen::SparseMatrix<double, Eigen::RowMajor>> a( + A->num_rows(), + A->num_cols(), + A->num_nonzeros(), + A->mutable_rows(), + A->mutable_cols(), + A->mutable_values()); Eigen::SparseMatrix<double> lhs = a.transpose() * a; Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>> solver; diff --git a/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.h b/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.h index 36118bab1a5..6f73c961212 100644 --- a/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.h +++ b/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.h @@ -36,9 +36,10 @@ // This include must come before any #ifndef check on Ceres compile options. // clang-format off -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" // clang-format on +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { @@ -53,12 +54,10 @@ class CompressedRowSparseMatrix; // // TODO(alex): Add support for Accelerate sparse solvers: // https://github.com/ceres-solver/ceres-solver/issues/397 -class DynamicSparseNormalCholeskySolver +class CERES_NO_EXPORT DynamicSparseNormalCholeskySolver : public CompressedRowSparseMatrixSolver { public: - explicit DynamicSparseNormalCholeskySolver( - const LinearSolver::Options& options); - virtual ~DynamicSparseNormalCholeskySolver() {} + explicit DynamicSparseNormalCholeskySolver(LinearSolver::Options options); private: LinearSolver::Summary SolveImpl(CompressedRowSparseMatrix* A, diff --git a/extern/ceres/internal/ceres/eigensparse.cc b/extern/ceres/internal/ceres/eigensparse.cc index 22ed2c43b5d..81668c82e67 100644 --- a/extern/ceres/internal/ceres/eigensparse.cc +++ b/extern/ceres/internal/ceres/eigensparse.cc @@ -30,6 +30,8 @@ #include "ceres/eigensparse.h" +#include <memory> + #ifdef CERES_USE_EIGEN_SPARSE #include <sstream> @@ -45,10 +47,9 @@ namespace internal { // TODO(sameeragarwal): Use enable_if to clean up the implementations // for when Scalar == double. template <typename Solver> -class EigenSparseCholeskyTemplate : public SparseCholesky { +class EigenSparseCholeskyTemplate final : public SparseCholesky { public: - EigenSparseCholeskyTemplate() : analyzed_(false) {} - virtual ~EigenSparseCholeskyTemplate() {} + EigenSparseCholeskyTemplate() = default; CompressedRowSparseMatrix::StorageType StorageType() const final { return CompressedRowSparseMatrix::LOWER_TRIANGULAR; } @@ -83,7 +84,7 @@ class EigenSparseCholeskyTemplate : public SparseCholesky { LinearSolverTerminationType Solve(const double* rhs_ptr, double* solution_ptr, - std::string* message) { + std::string* message) override { CHECK(analyzed_) << "Solve called without a call to Factorize first."; scalar_rhs_ = ConstVectorRef(rhs_ptr, solver_.cols()) @@ -109,7 +110,7 @@ class EigenSparseCholeskyTemplate : public SparseCholesky { std::string* message) final { CHECK_EQ(lhs->storage_type(), StorageType()); - typename Solver::Scalar* values_ptr = NULL; + typename Solver::Scalar* values_ptr = nullptr; if (std::is_same<typename Solver::Scalar, double>::value) { values_ptr = reinterpret_cast<typename Solver::Scalar*>(lhs->mutable_values()); @@ -122,7 +123,7 @@ class EigenSparseCholeskyTemplate : public SparseCholesky { values_ptr = values_.data(); } - Eigen::MappedSparseMatrix<typename Solver::Scalar, Eigen::ColMajor> + Eigen::Map<Eigen::SparseMatrix<typename Solver::Scalar, Eigen::ColMajor>> eigen_lhs(lhs->num_rows(), lhs->num_rows(), lhs->num_nonzeros(), @@ -135,54 +136,46 @@ class EigenSparseCholeskyTemplate : public SparseCholesky { private: Eigen::Matrix<typename Solver::Scalar, Eigen::Dynamic, 1> values_, scalar_rhs_, scalar_solution_; - bool analyzed_; + bool analyzed_{false}; Solver solver_; }; std::unique_ptr<SparseCholesky> EigenSparseCholesky::Create( const OrderingType ordering_type) { - std::unique_ptr<SparseCholesky> sparse_cholesky; - - typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>, - Eigen::Upper, - Eigen::AMDOrdering<int>> - WithAMDOrdering; - typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>, - Eigen::Upper, - Eigen::NaturalOrdering<int>> - WithNaturalOrdering; + using WithAMDOrdering = Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>, + Eigen::Upper, + Eigen::AMDOrdering<int>>; + using WithNaturalOrdering = + Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>, + Eigen::Upper, + Eigen::NaturalOrdering<int>>; + if (ordering_type == AMD) { - sparse_cholesky.reset(new EigenSparseCholeskyTemplate<WithAMDOrdering>()); + return std::make_unique<EigenSparseCholeskyTemplate<WithAMDOrdering>>(); } else { - sparse_cholesky.reset( - new EigenSparseCholeskyTemplate<WithNaturalOrdering>()); + return std::make_unique<EigenSparseCholeskyTemplate<WithNaturalOrdering>>(); } - return sparse_cholesky; } -EigenSparseCholesky::~EigenSparseCholesky() {} +EigenSparseCholesky::~EigenSparseCholesky() = default; std::unique_ptr<SparseCholesky> FloatEigenSparseCholesky::Create( const OrderingType ordering_type) { - std::unique_ptr<SparseCholesky> sparse_cholesky; - typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<float>, - Eigen::Upper, - Eigen::AMDOrdering<int>> - WithAMDOrdering; - typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<float>, - Eigen::Upper, - Eigen::NaturalOrdering<int>> - WithNaturalOrdering; + using WithAMDOrdering = Eigen::SimplicialLDLT<Eigen::SparseMatrix<float>, + Eigen::Upper, + Eigen::AMDOrdering<int>>; + using WithNaturalOrdering = + Eigen::SimplicialLDLT<Eigen::SparseMatrix<float>, + Eigen::Upper, + Eigen::NaturalOrdering<int>>; if (ordering_type == AMD) { - sparse_cholesky.reset(new EigenSparseCholeskyTemplate<WithAMDOrdering>()); + return std::make_unique<EigenSparseCholeskyTemplate<WithAMDOrdering>>(); } else { - sparse_cholesky.reset( - new EigenSparseCholeskyTemplate<WithNaturalOrdering>()); + return std::make_unique<EigenSparseCholeskyTemplate<WithNaturalOrdering>>(); } - return sparse_cholesky; } -FloatEigenSparseCholesky::~FloatEigenSparseCholesky() {} +FloatEigenSparseCholesky::~FloatEigenSparseCholesky() = default; } // namespace internal } // namespace ceres diff --git a/extern/ceres/internal/ceres/eigensparse.h b/extern/ceres/internal/ceres/eigensparse.h index bb89c2c11ac..c4a4142e586 100644 --- a/extern/ceres/internal/ceres/eigensparse.h +++ b/extern/ceres/internal/ceres/eigensparse.h @@ -34,7 +34,7 @@ #define CERES_INTERNAL_EIGENSPARSE_H_ // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifdef CERES_USE_EIGEN_SPARSE @@ -42,44 +42,45 @@ #include <string> #include "Eigen/SparseCore" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" #include "ceres/sparse_cholesky.h" namespace ceres { namespace internal { -class EigenSparseCholesky : public SparseCholesky { +class CERES_NO_EXPORT EigenSparseCholesky : public SparseCholesky { public: // Factory static std::unique_ptr<SparseCholesky> Create( const OrderingType ordering_type); // SparseCholesky interface. - virtual ~EigenSparseCholesky(); - virtual LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, - std::string* message) = 0; - virtual CompressedRowSparseMatrix::StorageType StorageType() const = 0; - virtual LinearSolverTerminationType Solve(const double* rhs, - double* solution, - std::string* message) = 0; + ~EigenSparseCholesky() override; + LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, + std::string* message) override = 0; + CompressedRowSparseMatrix::StorageType StorageType() const override = 0; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override = 0; }; // Even though the input is double precision linear system, this class // solves it by computing a single precision Cholesky factorization. -class FloatEigenSparseCholesky : public SparseCholesky { +class CERES_NO_EXPORT FloatEigenSparseCholesky : public SparseCholesky { public: // Factory static std::unique_ptr<SparseCholesky> Create( const OrderingType ordering_type); // SparseCholesky interface. - virtual ~FloatEigenSparseCholesky(); - virtual LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, - std::string* message) = 0; - virtual CompressedRowSparseMatrix::StorageType StorageType() const = 0; - virtual LinearSolverTerminationType Solve(const double* rhs, - double* solution, - std::string* message) = 0; + ~FloatEigenSparseCholesky() override; + LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, + std::string* message) override = 0; + CompressedRowSparseMatrix::StorageType StorageType() const override = 0; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override = 0; }; } // namespace internal diff --git a/extern/ceres/internal/ceres/evaluation_callback.cc b/extern/ceres/internal/ceres/evaluation_callback.cc new file mode 100644 index 00000000000..77591a8c621 --- /dev/null +++ b/extern/ceres/internal/ceres/evaluation_callback.cc @@ -0,0 +1,37 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: mierle@gmail.com (Keir Mierle) + +#include "ceres/evaluation_callback.h" + +namespace ceres { + +EvaluationCallback::~EvaluationCallback() = default; + +} // namespace ceres diff --git a/extern/ceres/internal/ceres/evaluator.cc b/extern/ceres/internal/ceres/evaluator.cc index 516874184d9..52d0f09e5df 100644 --- a/extern/ceres/internal/ceres/evaluator.cc +++ b/extern/ceres/internal/ceres/evaluator.cc @@ -30,6 +30,7 @@ #include "ceres/evaluator.h" +#include <memory> #include <vector> #include "ceres/block_evaluate_preparer.h" @@ -40,7 +41,7 @@ #include "ceres/dense_jacobian_writer.h" #include "ceres/dynamic_compressed_row_finalizer.h" #include "ceres/dynamic_compressed_row_jacobian_writer.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/program_evaluator.h" #include "ceres/scratch_evaluate_preparer.h" #include "glog/logging.h" @@ -48,38 +49,42 @@ namespace ceres { namespace internal { -Evaluator::~Evaluator() {} +Evaluator::~Evaluator() = default; -Evaluator* Evaluator::Create(const Evaluator::Options& options, - Program* program, - std::string* error) { - CHECK(options.context != NULL); +std::unique_ptr<Evaluator> Evaluator::Create(const Evaluator::Options& options, + Program* program, + std::string* error) { + CHECK(options.context != nullptr); switch (options.linear_solver_type) { case DENSE_QR: case DENSE_NORMAL_CHOLESKY: - return new ProgramEvaluator<ScratchEvaluatePreparer, DenseJacobianWriter>( + return std::make_unique< + ProgramEvaluator<ScratchEvaluatePreparer, DenseJacobianWriter>>( options, program); case DENSE_SCHUR: case SPARSE_SCHUR: case ITERATIVE_SCHUR: case CGNR: - return new ProgramEvaluator<BlockEvaluatePreparer, BlockJacobianWriter>( + return std::make_unique< + ProgramEvaluator<BlockEvaluatePreparer, BlockJacobianWriter>>( options, program); case SPARSE_NORMAL_CHOLESKY: if (options.dynamic_sparsity) { - return new ProgramEvaluator<ScratchEvaluatePreparer, - DynamicCompressedRowJacobianWriter, - DynamicCompressedRowJacobianFinalizer>( - options, program); + return std::make_unique< + ProgramEvaluator<ScratchEvaluatePreparer, + DynamicCompressedRowJacobianWriter, + DynamicCompressedRowJacobianFinalizer>>(options, + program); } else { - return new ProgramEvaluator<BlockEvaluatePreparer, BlockJacobianWriter>( + return std::make_unique< + ProgramEvaluator<BlockEvaluatePreparer, BlockJacobianWriter>>( options, program); } default: *error = "Invalid Linear Solver Type. Unable to create evaluator."; - return NULL; + return nullptr; } } diff --git a/extern/ceres/internal/ceres/evaluator.h b/extern/ceres/internal/ceres/evaluator.h index 9cf42593e95..68a4fb28a55 100644 --- a/extern/ceres/internal/ceres/evaluator.h +++ b/extern/ceres/internal/ceres/evaluator.h @@ -33,12 +33,14 @@ #define CERES_INTERNAL_EVALUATOR_H_ #include <map> +#include <memory> #include <string> #include <vector> #include "ceres/context_impl.h" #include "ceres/execution_summary.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/types.h" namespace ceres { @@ -54,8 +56,8 @@ class SparseMatrix; // The Evaluator interface offers a way to interact with a least squares cost // function that is useful for an optimizer that wants to minimize the least // squares objective. This insulates the optimizer from issues like Jacobian -// storage, parameterization, etc. -class CERES_EXPORT_INTERNAL Evaluator { +// storage, manifolds, etc. +class CERES_NO_EXPORT Evaluator { public: virtual ~Evaluator(); @@ -68,9 +70,9 @@ class CERES_EXPORT_INTERNAL Evaluator { EvaluationCallback* evaluation_callback = nullptr; }; - static Evaluator* Create(const Options& options, - Program* program, - std::string* error); + static std::unique_ptr<Evaluator> Create(const Options& options, + Program* program, + std::string* error); // Build and return a sparse matrix for storing and working with the Jacobian // of the objective function. The jacobian has dimensions @@ -88,7 +90,7 @@ class CERES_EXPORT_INTERNAL Evaluator { // the jacobian for use with CHOLMOD, where as BlockOptimizationProblem // creates a BlockSparseMatrix representation of the jacobian for use in the // Schur complement based methods. - virtual SparseMatrix* CreateJacobian() const = 0; + virtual std::unique_ptr<SparseMatrix> CreateJacobian() const = 0; // Options struct to control Evaluator::Evaluate; struct EvaluateOptions { @@ -102,10 +104,10 @@ class CERES_EXPORT_INTERNAL Evaluator { // Evaluate the cost function for the given state. Returns the cost, // residuals, and jacobian in the corresponding arguments. Both residuals and - // jacobian are optional; to avoid computing them, pass NULL. + // jacobian are optional; to avoid computing them, pass nullptr. // - // If non-NULL, the Jacobian must have a suitable sparsity pattern; only the - // values array of the jacobian is modified. + // If non-nullptr, the Jacobian must have a suitable sparsity pattern; only + // the values array of the jacobian is modified. // // state is an array of size NumParameters(), cost is a pointer to a single // double, and residuals is an array of doubles of size NumResiduals(). @@ -131,13 +133,13 @@ class CERES_EXPORT_INTERNAL Evaluator { // Make a change delta (of size NumEffectiveParameters()) to state (of size // NumParameters()) and store the result in state_plus_delta. // - // In the case that there are no parameterizations used, this is equivalent to + // In the case that there are no manifolds used, this is equivalent to // // state_plus_delta[i] = state[i] + delta[i] ; // - // however, the mapping is more complicated in the case of parameterizations + // however, the mapping is more complicated in the case of manifolds // like quaternions. This is the same as the "Plus()" operation in - // local_parameterization.h, but operating over the entire state vector for a + // manifold.h, but operating over the entire state vector for a // problem. virtual bool Plus(const double* state, const double* delta, @@ -147,7 +149,7 @@ class CERES_EXPORT_INTERNAL Evaluator { virtual int NumParameters() const = 0; // This is the effective number of parameters that the optimizer may adjust. - // This applies when there are parameterizations on some of the parameters. + // This applies when there are manifolds on some of the parameters. virtual int NumEffectiveParameters() const = 0; // The number of residuals in the optimization problem. @@ -158,11 +160,13 @@ class CERES_EXPORT_INTERNAL Evaluator { // life time issues. Further, these calls are not expected to be // frequent or performance sensitive. virtual std::map<std::string, CallStatistics> Statistics() const { - return std::map<std::string, CallStatistics>(); + return {}; } }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_EVALUATOR_H_ diff --git a/extern/ceres/internal/ceres/execution_summary.h b/extern/ceres/internal/ceres/execution_summary.h index 17fd882af03..fbee75fc0cb 100644 --- a/extern/ceres/internal/ceres/execution_summary.h +++ b/extern/ceres/internal/ceres/execution_summary.h @@ -34,17 +34,18 @@ #include <map> #include <mutex> #include <string> +#include <utility> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/wall_time.h" namespace ceres { namespace internal { struct CallStatistics { - CallStatistics() : time(0.), calls(0) {} - double time; - int calls; + CallStatistics() = default; + double time{0.}; + int calls{0}; }; // Struct used by various objects to report statistics about their @@ -69,8 +70,10 @@ class ExecutionSummary { class ScopedExecutionTimer { public: - ScopedExecutionTimer(const std::string& name, ExecutionSummary* summary) - : start_time_(WallTimeInSeconds()), name_(name), summary_(summary) {} + ScopedExecutionTimer(std::string name, ExecutionSummary* summary) + : start_time_(WallTimeInSeconds()), + name_(std::move(name)), + summary_(summary) {} ~ScopedExecutionTimer() { summary_->IncrementTimeBy(name_, WallTimeInSeconds() - start_time_); diff --git a/extern/ceres/internal/ceres/file.h b/extern/ceres/internal/ceres/file.h index c0015df60f0..bd13128aedf 100644 --- a/extern/ceres/internal/ceres/file.h +++ b/extern/ceres/internal/ceres/file.h @@ -35,21 +35,26 @@ #include <string> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { +CERES_NO_EXPORT void WriteStringToFileOrDie(const std::string& data, const std::string& filename); +CERES_NO_EXPORT void ReadFileToStringOrDie(const std::string& filename, std::string* data); // Join two path components, adding a slash if necessary. If basename is an // absolute path then JoinPath ignores dirname and simply returns basename. -CERES_EXPORT_INTERNAL std::string JoinPath(const std::string& dirname, - const std::string& basename); +CERES_NO_EXPORT +std::string JoinPath(const std::string& dirname, const std::string& basename); } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_FILE_H_ diff --git a/extern/ceres/internal/ceres/blas.cc b/extern/ceres/internal/ceres/first_order_function.cc index f8d006e3069..26f13488a1d 100644 --- a/extern/ceres/internal/ceres/blas.cc +++ b/extern/ceres/internal/ceres/first_order_function.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -28,55 +28,10 @@ // // Author: sameeragarwal@google.com (Sameer Agarwal) -#include "ceres/blas.h" - -#include "ceres/internal/port.h" -#include "glog/logging.h" - -#ifndef CERES_NO_LAPACK -extern "C" void dsyrk_(char* uplo, - char* trans, - int* n, - int* k, - double* alpha, - double* a, - int* lda, - double* beta, - double* c, - int* ldc); -#endif +#include "ceres/first_order_function.h" namespace ceres { -namespace internal { -void BLAS::SymmetricRankKUpdate(int num_rows, - int num_cols, - const double* a, - bool transpose, - double alpha, - double beta, - double* c) { -#ifdef CERES_NO_LAPACK - LOG(FATAL) << "Ceres was built without a BLAS library."; -#else - char uplo = 'L'; - char trans = transpose ? 'T' : 'N'; - int n = transpose ? num_cols : num_rows; - int k = transpose ? num_rows : num_cols; - int lda = k; - int ldc = n; - dsyrk_(&uplo, - &trans, - &n, - &k, - &alpha, - const_cast<double*>(a), - &lda, - &beta, - c, - &ldc); -#endif -} +FirstOrderFunction::~FirstOrderFunction() = default; -} // namespace internal } // namespace ceres diff --git a/extern/ceres/internal/ceres/float_cxsparse.cc b/extern/ceres/internal/ceres/float_cxsparse.cc index 6c688303444..a6d5e811efd 100644 --- a/extern/ceres/internal/ceres/float_cxsparse.cc +++ b/extern/ceres/internal/ceres/float_cxsparse.cc @@ -30,6 +30,8 @@ #include "ceres/float_cxsparse.h" +#include <memory> + #if !defined(CERES_NO_CXSPARSE) namespace ceres { @@ -38,7 +40,7 @@ namespace internal { std::unique_ptr<SparseCholesky> FloatCXSparseCholesky::Create( OrderingType ordering_type) { LOG(FATAL) << "FloatCXSparseCholesky is not available."; - return std::unique_ptr<SparseCholesky>(); + return {}; } } // namespace internal diff --git a/extern/ceres/internal/ceres/float_cxsparse.h b/extern/ceres/internal/ceres/float_cxsparse.h index 9a274c23636..8b4514acb18 100644 --- a/extern/ceres/internal/ceres/float_cxsparse.h +++ b/extern/ceres/internal/ceres/float_cxsparse.h @@ -32,12 +32,13 @@ #define CERES_INTERNAL_FLOAT_CXSPARSE_H_ // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #if !defined(CERES_NO_CXSPARSE) #include <memory> +#include "ceres/internal/export.h" #include "ceres/sparse_cholesky.h" namespace ceres { @@ -45,7 +46,7 @@ namespace internal { // Fake implementation of a single precision Sparse Cholesky using // CXSparse. -class FloatCXSparseCholesky : public SparseCholesky { +class CERES_NO_EXPORT FloatCXSparseCholesky : public SparseCholesky { public: static std::unique_ptr<SparseCholesky> Create(OrderingType ordering_type); }; diff --git a/extern/ceres/internal/ceres/float_suitesparse.cc b/extern/ceres/internal/ceres/float_suitesparse.cc index 03604572b5c..dc1d0e45bdb 100644 --- a/extern/ceres/internal/ceres/float_suitesparse.cc +++ b/extern/ceres/internal/ceres/float_suitesparse.cc @@ -30,6 +30,8 @@ #include "ceres/float_suitesparse.h" +#include <memory> + #if !defined(CERES_NO_SUITESPARSE) namespace ceres { @@ -38,7 +40,7 @@ namespace internal { std::unique_ptr<SparseCholesky> FloatSuiteSparseCholesky::Create( OrderingType ordering_type) { LOG(FATAL) << "FloatSuiteSparseCholesky is not available."; - return std::unique_ptr<SparseCholesky>(); + return {}; } } // namespace internal diff --git a/extern/ceres/internal/ceres/float_suitesparse.h b/extern/ceres/internal/ceres/float_suitesparse.h index c436da43f86..7e76799e241 100644 --- a/extern/ceres/internal/ceres/float_suitesparse.h +++ b/extern/ceres/internal/ceres/float_suitesparse.h @@ -33,11 +33,12 @@ // This include must come before any #ifndef check on Ceres compile options. // clang-format off -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" // clang-format on #include <memory> +#include "ceres/internal/export.h" #include "ceres/sparse_cholesky.h" #if !defined(CERES_NO_SUITESPARSE) @@ -47,7 +48,7 @@ namespace internal { // Fake implementation of a single precision Sparse Cholesky using // SuiteSparse. -class FloatSuiteSparseCholesky : public SparseCholesky { +class CERES_NO_EXPORT FloatSuiteSparseCholesky : public SparseCholesky { public: static std::unique_ptr<SparseCholesky> Create(OrderingType ordering_type); }; diff --git a/extern/ceres/internal/ceres/function_sample.h b/extern/ceres/internal/ceres/function_sample.h index 3bcea1bc5ff..63ffc8ff8fc 100644 --- a/extern/ceres/internal/ceres/function_sample.h +++ b/extern/ceres/internal/ceres/function_sample.h @@ -33,8 +33,9 @@ #include <string> +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -47,7 +48,7 @@ namespace internal { // line/direction. FunctionSample contains the information in two // ways. Information in the ambient space and information along the // direction of search. -struct CERES_EXPORT_INTERNAL FunctionSample { +struct CERES_NO_EXPORT FunctionSample { FunctionSample(); FunctionSample(double x, double value); FunctionSample(double x, double value, double gradient); @@ -90,4 +91,6 @@ struct CERES_EXPORT_INTERNAL FunctionSample { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_FUNCTION_SAMPLE_H_ diff --git a/extern/ceres/internal/ceres/generate_template_specializations.py b/extern/ceres/internal/ceres/generate_template_specializations.py deleted file mode 100644 index 74e46c28b78..00000000000 --- a/extern/ceres/internal/ceres/generate_template_specializations.py +++ /dev/null @@ -1,246 +0,0 @@ -# Ceres Solver - A fast non-linear least squares minimizer -# Copyright 2015 Google Inc. All rights reserved. -# http://ceres-solver.org/ -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Google Inc. nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# Author: sameeragarwal@google.com (Sameer Agarwal) -# -# Script for explicitly generating template specialization of the -# SchurEliminator class. It is a rather large class -# and the number of explicit instantiations is also large. Explicitly -# generating these instantiations in separate .cc files breaks the -# compilation into separate compilation unit rather than one large cc -# file which takes 2+GB of RAM to compile. -# -# This script creates three sets of files. -# -# 1. schur_eliminator_x_x_x.cc and partitioned_matrix_view_x_x_x.cc -# where, the x indicates the template parameters and -# -# 2. schur_eliminator.cc & partitioned_matrix_view.cc -# -# that contains a factory function for instantiating these classes -# based on runtime parameters. -# -# 3. schur_templates.cc -# -# that contains a function which can be queried to determine what -# template specializations are available. -# -# The following list of tuples, specializations indicates the set of -# specializations that is generated. -SPECIALIZATIONS = [(2, 2, 2), - (2, 2, 3), - (2, 2, 4), - (2, 2, "Eigen::Dynamic"), - (2, 3, 3), - (2, 3, 4), - (2, 3, 6), - (2, 3, 9), - (2, 3, "Eigen::Dynamic"), - (2, 4, 3), - (2, 4, 4), - (2, 4, 6), - (2, 4, 8), - (2, 4, 9), - (2, 4, "Eigen::Dynamic"), - (2, "Eigen::Dynamic", "Eigen::Dynamic"), - (3, 3, 3), - (4, 4, 2), - (4, 4, 3), - (4, 4, 4), - (4, 4, "Eigen::Dynamic")] - -import schur_eliminator_template -import partitioned_matrix_view_template -import os -import glob - -def SuffixForSize(size): - if size == "Eigen::Dynamic": - return "d" - return str(size) - -def SpecializationFilename(prefix, row_block_size, e_block_size, f_block_size): - return "_".join([prefix] + map(SuffixForSize, (row_block_size, - e_block_size, - f_block_size))) - -def GenerateFactoryConditional(row_block_size, e_block_size, f_block_size): - conditionals = [] - if (row_block_size != "Eigen::Dynamic"): - conditionals.append("(options.row_block_size == %s)" % row_block_size) - if (e_block_size != "Eigen::Dynamic"): - conditionals.append("(options.e_block_size == %s)" % e_block_size) - if (f_block_size != "Eigen::Dynamic"): - conditionals.append("(options.f_block_size == %s)" % f_block_size) - if (len(conditionals) == 0): - return "%s" - - if (len(conditionals) == 1): - return " if " + conditionals[0] + " {\n %s\n }\n" - - return " if (" + " &&\n ".join(conditionals) + ") {\n %s\n }\n" - -def Specialize(name, data): - """ - Generate specialization code and the conditionals to instantiate it. - """ - - # Specialization files - for row_block_size, e_block_size, f_block_size in SPECIALIZATIONS: - output = SpecializationFilename("generated/" + name, - row_block_size, - e_block_size, - f_block_size) + ".cc" - - with open(output, "w") as f: - f.write(data["HEADER"]) - f.write(data["SPECIALIZATION_FILE"] % - (row_block_size, e_block_size, f_block_size)) - - # Generate the _d_d_d specialization. - output = SpecializationFilename("generated/" + name, - "Eigen::Dynamic", - "Eigen::Dynamic", - "Eigen::Dynamic") + ".cc" - with open(output, "w") as f: - f.write(data["HEADER"]) - f.write(data["DYNAMIC_FILE"] % - ("Eigen::Dynamic", "Eigen::Dynamic", "Eigen::Dynamic")) - - # Factory - with open(name + ".cc", "w") as f: - f.write(data["HEADER"]) - f.write(data["FACTORY_FILE_HEADER"]) - for row_block_size, e_block_size, f_block_size in SPECIALIZATIONS: - factory_conditional = GenerateFactoryConditional( - row_block_size, e_block_size, f_block_size) - factory = data["FACTORY"] % (row_block_size, e_block_size, f_block_size) - f.write(factory_conditional % factory); - f.write(data["FACTORY_FOOTER"]) - -QUERY_HEADER = """// Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2017 Google Inc. All rights reserved. -// http://ceres-solver.org/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Author: sameeragarwal@google.com (Sameer Agarwal) -// -// What template specializations are available. -// -// ======================================== -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -//========================================= -// -// This file is generated using generate_template_specializations.py. -""" - -QUERY_FILE_HEADER = """ -#include "ceres/internal/eigen.h" -#include "ceres/schur_templates.h" - -namespace ceres { -namespace internal { - -void GetBestSchurTemplateSpecialization(int* row_block_size, - int* e_block_size, - int* f_block_size) { - LinearSolver::Options options; - options.row_block_size = *row_block_size; - options.e_block_size = *e_block_size; - options.f_block_size = *f_block_size; - *row_block_size = Eigen::Dynamic; - *e_block_size = Eigen::Dynamic; - *f_block_size = Eigen::Dynamic; -#ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION -""" - -QUERY_FOOTER = """ -#endif - return; -} - -} // namespace internal -} // namespace ceres -""" - -QUERY_ACTION = """ *row_block_size = %s; - *e_block_size = %s; - *f_block_size = %s; - return;""" - -def GenerateQueryFile(): - """ - Generate file that allows querying for available template specializations. - """ - - with open("schur_templates.cc", "w") as f: - f.write(QUERY_HEADER) - f.write(QUERY_FILE_HEADER) - for row_block_size, e_block_size, f_block_size in SPECIALIZATIONS: - factory_conditional = GenerateFactoryConditional( - row_block_size, e_block_size, f_block_size) - action = QUERY_ACTION % (row_block_size, e_block_size, f_block_size) - f.write(factory_conditional % action) - f.write(QUERY_FOOTER) - - -if __name__ == "__main__": - for f in glob.glob("generated/*"): - os.remove(f) - - Specialize("schur_eliminator", - schur_eliminator_template.__dict__) - Specialize("partitioned_matrix_view", - partitioned_matrix_view_template.__dict__) - GenerateQueryFile() diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_2.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_2.cc index f5753bef544..7b4ed167d05 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_2.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_2.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_3.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_3.cc index a7a9b5231cf..0f012515a95 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_3.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_4.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_4.cc index faf6c4a754a..bdbe91c43f6 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_4.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_4.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_d.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_d.cc index 92fd4cddf43..71f293b5512 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_d.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_3.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_3.cc index 2df314f137a..a6ea7761c9a 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_3.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_4.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_4.cc index ff1ca3e7f1f..e712678a28a 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_4.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_4.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_6.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_6.cc index 5041df9152d..3aff26e657b 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_6.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_6.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_9.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_9.cc index c0b72fec8b8..6cd239bfd9a 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_9.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_9.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_d.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_d.cc index 8a3c162ab7e..68c50552d42 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_d.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_3.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_3.cc index 0e69ca6404d..88c5e29c6f8 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_3.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_4.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_4.cc index ba9bb615291..b9487834441 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_4.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_4.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_6.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_6.cc index 1acdb9b21d5..7f044ef628b 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_6.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_6.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_8.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_8.cc index 888ff99557d..7394e7998e7 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_8.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_8.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_9.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_9.cc index bd4dde3d207..263f1fb36f1 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_9.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_9.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_d.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_d.cc index 6d3516fc6d5..d47634e0f40 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_d.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_d_d.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_d_d.cc index 77d22ed6bc2..0944cdcbfda 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_d_d.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_d_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_3_3_3.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_3_3_3.cc index aeb456c6e2a..23674031bb9 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_3_3_3.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_3_3_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_2.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_2.cc index bb240b9e3f6..d5268cac481 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_2.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_2.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_3.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_3.cc index 5d47543644d..67e098fc6f3 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_3.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_4.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_4.cc index e14f980933d..5fe28caee8c 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_4.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_4.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_d.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_d.cc index 9ec50563ac8..d87c76d0aa4 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_d.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_2.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_2.cc index 289a809acb7..dc47a2e6d8e 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_2.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_2.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_3.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_3.cc index 20311ba843d..e2df6f63d2f 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_3.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_4.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_4.cc index 1f6a8ae4a0e..0b1ae949a09 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_4.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_4.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_d.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_d.cc index 08b18d357bd..0f7b6d78c7f 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_d.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_3.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_3.cc index 115b4c8cce1..e4ab8eb19bf 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_3.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_4.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_4.cc index c7035370424..d73d466b04c 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_4.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_4.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_6.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_6.cc index edb9afea969..800ee536bbf 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_6.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_6.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_9.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_9.cc index faa5c19f5c0..d38cd566082 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_9.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_9.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_d.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_d.cc index 81b6f975e7f..4ac4b8ac8b7 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_d.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_3.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_3.cc index 2cb2d15ac93..d5f5dbea4b4 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_3.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_4.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_4.cc index a78eff3aa02..d50a6d4002b 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_4.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_4.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_6.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_6.cc index e2534f235b6..f79fa4dd2f0 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_6.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_6.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_8.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_8.cc index 296a46273bc..972b000f1ba 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_8.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_8.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_9.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_9.cc index 0d0b04e686c..aa33e479bc5 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_9.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_9.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_d.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_d.cc index 797992660d7..a28ef15a522 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_d.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_d_d.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_d_d.cc index 189be043af8..43924279a39 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_d_d.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_d_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_3_3_3.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_3_3_3.cc index 35c14a8f4bd..7ff2a62341c 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_3_3_3.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_3_3_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_2.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_2.cc index 878500a2100..9008b816843 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_2.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_2.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_3.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_3.cc index c4b0959db6a..8e37df51bee 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_3.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_4.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_4.cc index 20df5343335..09d50813a8a 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_4.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_4.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_d.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_d.cc index 17368dca4f6..089df2d7e3e 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_d.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/gradient_checker.cc b/extern/ceres/internal/ceres/gradient_checker.cc index dadaaa08734..777001e013c 100644 --- a/extern/ceres/internal/ceres/gradient_checker.cc +++ b/extern/ceres/internal/ceres/gradient_checker.cc @@ -40,6 +40,7 @@ #include <vector> #include "ceres/is_close.h" +#include "ceres/manifold_adapter.h" #include "ceres/stringprintf.h" #include "ceres/types.h" @@ -53,15 +54,13 @@ using std::vector; namespace { // Evaluate the cost function and transform the returned Jacobians to -// the local space of the respective local parameterizations. -bool EvaluateCostFunction( - const ceres::CostFunction* function, - double const* const* parameters, - const std::vector<const ceres::LocalParameterization*>& - local_parameterizations, - Vector* residuals, - std::vector<Matrix>* jacobians, - std::vector<Matrix>* local_jacobians) { +// the tangent space of the respective local parameterizations. +bool EvaluateCostFunction(const CostFunction* function, + double const* const* parameters, + const std::vector<const Manifold*>& manifolds, + Vector* residuals, + std::vector<Matrix>* jacobians, + std::vector<Matrix>* local_jacobians) { CHECK(residuals != nullptr); CHECK(jacobians != nullptr); CHECK(local_jacobians != nullptr); @@ -69,20 +68,20 @@ bool EvaluateCostFunction( const vector<int32_t>& block_sizes = function->parameter_block_sizes(); const int num_parameter_blocks = block_sizes.size(); - // Allocate Jacobian matrices in local space. + // Allocate Jacobian matrices in tangent space. local_jacobians->resize(num_parameter_blocks); vector<double*> local_jacobian_data(num_parameter_blocks); for (int i = 0; i < num_parameter_blocks; ++i) { int block_size = block_sizes.at(i); - if (local_parameterizations.at(i) != NULL) { - block_size = local_parameterizations.at(i)->LocalSize(); + if (manifolds.at(i) != nullptr) { + block_size = manifolds.at(i)->TangentSize(); } local_jacobians->at(i).resize(function->num_residuals(), block_size); local_jacobians->at(i).setZero(); local_jacobian_data.at(i) = local_jacobians->at(i).data(); } - // Allocate Jacobian matrices in global space. + // Allocate Jacobian matrices in ambient space. jacobians->resize(num_parameter_blocks); vector<double*> jacobian_data(num_parameter_blocks); for (int i = 0; i < num_parameter_blocks; ++i) { @@ -100,18 +99,17 @@ bool EvaluateCostFunction( return false; } - // Convert Jacobians from global to local space. + // Convert Jacobians from ambient to local space. for (size_t i = 0; i < local_jacobians->size(); ++i) { - if (local_parameterizations.at(i) == NULL) { + if (manifolds.at(i) == nullptr) { local_jacobians->at(i) = jacobians->at(i); } else { - int global_size = local_parameterizations.at(i)->GlobalSize(); - int local_size = local_parameterizations.at(i)->LocalSize(); - CHECK_EQ(jacobians->at(i).cols(), global_size); - Matrix global_J_local(global_size, local_size); - local_parameterizations.at(i)->ComputeJacobian(parameters[i], - global_J_local.data()); - local_jacobians->at(i).noalias() = jacobians->at(i) * global_J_local; + int ambient_size = manifolds.at(i)->AmbientSize(); + int tangent_size = manifolds.at(i)->TangentSize(); + CHECK_EQ(jacobians->at(i).cols(), ambient_size); + Matrix ambient_J_tangent(ambient_size, tangent_size); + manifolds.at(i)->PlusJacobian(parameters[i], ambient_J_tangent.data()); + local_jacobians->at(i).noalias() = jacobians->at(i) * ambient_J_tangent; } } return true; @@ -122,20 +120,47 @@ GradientChecker::GradientChecker( const CostFunction* function, const vector<const LocalParameterization*>* local_parameterizations, const NumericDiffOptions& options) + : delete_manifolds_(true), function_(function) { + CHECK(function != nullptr); + manifolds_.resize(function->parameter_block_sizes().size(), nullptr); + + // Wrap the local parameterization into manifold objects using + // ManifoldAdapter. + for (int i = 0; i < manifolds_.size(); ++i) { + const LocalParameterization* local_param = local_parameterizations->at(i); + if (local_param == nullptr) { + continue; + } + manifolds_[i] = new internal::ManifoldAdapter(local_param); + } + + auto finite_diff_cost_function = + std::make_unique<DynamicNumericDiffCostFunction<CostFunction, RIDDERS>>( + function, DO_NOT_TAKE_OWNERSHIP, options); + const vector<int32_t>& parameter_block_sizes = + function->parameter_block_sizes(); + for (int32_t parameter_block_size : parameter_block_sizes) { + finite_diff_cost_function->AddParameterBlock(parameter_block_size); + } + finite_diff_cost_function->SetNumResiduals(function->num_residuals()); + + finite_diff_cost_function_ = std::move(finite_diff_cost_function); +} + +GradientChecker::GradientChecker(const CostFunction* function, + const vector<const Manifold*>* manifolds, + const NumericDiffOptions& options) : function_(function) { CHECK(function != nullptr); - if (local_parameterizations != NULL) { - local_parameterizations_ = *local_parameterizations; + if (manifolds != nullptr) { + manifolds_ = *manifolds; } else { - local_parameterizations_.resize(function->parameter_block_sizes().size(), - NULL); + manifolds_.resize(function->parameter_block_sizes().size(), nullptr); } - DynamicNumericDiffCostFunction<CostFunction, RIDDERS>* - finite_diff_cost_function = - new DynamicNumericDiffCostFunction<CostFunction, RIDDERS>( - function, DO_NOT_TAKE_OWNERSHIP, options); - finite_diff_cost_function_.reset(finite_diff_cost_function); + auto finite_diff_cost_function = + std::make_unique<DynamicNumericDiffCostFunction<CostFunction, RIDDERS>>( + function, DO_NOT_TAKE_OWNERSHIP, options); const vector<int32_t>& parameter_block_sizes = function->parameter_block_sizes(); const int num_parameter_blocks = parameter_block_sizes.size(); @@ -143,6 +168,16 @@ GradientChecker::GradientChecker( finite_diff_cost_function->AddParameterBlock(parameter_block_sizes[i]); } finite_diff_cost_function->SetNumResiduals(function->num_residuals()); + + finite_diff_cost_function_ = std::move(finite_diff_cost_function); +} + +GradientChecker::~GradientChecker() { + if (delete_manifolds_) { + for (const auto m : manifolds_) { + delete m; + } + } } bool GradientChecker::Probe(double const* const* parameters, @@ -154,7 +189,7 @@ bool GradientChecker::Probe(double const* const* parameters, // provided an output argument. ProbeResults* results; ProbeResults results_local; - if (results_param != NULL) { + if (results_param != nullptr) { results = results_param; results->residuals.resize(0); results->jacobians.clear(); @@ -173,7 +208,7 @@ bool GradientChecker::Probe(double const* const* parameters, vector<Matrix>& local_jacobians = results->local_jacobians; if (!EvaluateCostFunction(function_, parameters, - local_parameterizations_, + manifolds_, &results->residuals, &jacobians, &local_jacobians)) { @@ -187,7 +222,7 @@ bool GradientChecker::Probe(double const* const* parameters, Vector finite_diff_residuals; if (!EvaluateCostFunction(finite_diff_cost_function_.get(), parameters, - local_parameterizations_, + manifolds_, &finite_diff_residuals, &numeric_jacobians, &local_numeric_jacobians)) { @@ -205,8 +240,8 @@ bool GradientChecker::Probe(double const* const* parameters, if (!IsClose(results->residuals[i], finite_diff_residuals[i], relative_precision, - NULL, - NULL)) { + nullptr, + nullptr)) { results->error_log = "Function evaluation with and without Jacobians " "resulted in different residuals."; diff --git a/extern/ceres/internal/ceres/gradient_checking_cost_function.cc b/extern/ceres/internal/ceres/gradient_checking_cost_function.cc index 2eb6d627167..1c3b318ed04 100644 --- a/extern/ceres/internal/ceres/gradient_checking_cost_function.cc +++ b/extern/ceres/internal/ceres/gradient_checking_cost_function.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -34,8 +34,10 @@ #include <algorithm> #include <cmath> #include <cstdint> +#include <memory> #include <numeric> #include <string> +#include <utility> #include <vector> #include "ceres/dynamic_numeric_diff_cost_function.h" @@ -60,19 +62,18 @@ using std::vector; namespace { -class GradientCheckingCostFunction : public CostFunction { +class GradientCheckingCostFunction final : public CostFunction { public: - GradientCheckingCostFunction( - const CostFunction* function, - const std::vector<const LocalParameterization*>* local_parameterizations, - const NumericDiffOptions& options, - double relative_precision, - const string& extra_info, - GradientCheckingIterationCallback* callback) + GradientCheckingCostFunction(const CostFunction* function, + const std::vector<const Manifold*>* manifolds, + const NumericDiffOptions& options, + double relative_precision, + string extra_info, + GradientCheckingIterationCallback* callback) : function_(function), - gradient_checker_(function, local_parameterizations, options), + gradient_checker_(function, manifolds, options), relative_precision_(relative_precision), - extra_info_(extra_info), + extra_info_(std::move(extra_info)), callback_(callback) { CHECK(callback_ != nullptr); const vector<int32_t>& parameter_block_sizes = @@ -81,14 +82,12 @@ class GradientCheckingCostFunction : public CostFunction { set_num_residuals(function->num_residuals()); } - virtual ~GradientCheckingCostFunction() {} - bool Evaluate(double const* const* parameters, double* residuals, double** jacobians) const final { if (!jacobians) { // Nothing to check in this case; just forward. - return function_->Evaluate(parameters, residuals, NULL); + return function_->Evaluate(parameters, residuals, nullptr); } GradientChecker::ProbeResults results; @@ -108,7 +107,7 @@ class GradientCheckingCostFunction : public CostFunction { // Copy the original jacobian blocks into the jacobians array. const vector<int32_t>& block_sizes = function_->parameter_block_sizes(); for (int k = 0; k < block_sizes.size(); k++) { - if (jacobians[k] != NULL) { + if (jacobians[k] != nullptr) { MatrixRef(jacobians[k], results.jacobians[k].rows(), results.jacobians[k].cols()) = results.jacobians[k]; @@ -145,6 +144,7 @@ CallbackReturnType GradientCheckingIterationCallback::operator()( } return SOLVER_CONTINUE; } + void GradientCheckingIterationCallback::SetGradientErrorDetected( std::string& error_log) { std::lock_guard<std::mutex> l(mutex_); @@ -152,9 +152,9 @@ void GradientCheckingIterationCallback::SetGradientErrorDetected( error_log_ += "\n" + error_log; } -CostFunction* CreateGradientCheckingCostFunction( +std::unique_ptr<CostFunction> CreateGradientCheckingCostFunction( const CostFunction* cost_function, - const std::vector<const LocalParameterization*>* local_parameterizations, + const std::vector<const Manifold*>* manifolds, double relative_step_size, double relative_precision, const std::string& extra_info, @@ -162,51 +162,48 @@ CostFunction* CreateGradientCheckingCostFunction( NumericDiffOptions numeric_diff_options; numeric_diff_options.relative_step_size = relative_step_size; - return new GradientCheckingCostFunction(cost_function, - local_parameterizations, - numeric_diff_options, - relative_precision, - extra_info, - callback); + return std::make_unique<GradientCheckingCostFunction>(cost_function, + manifolds, + numeric_diff_options, + relative_precision, + extra_info, + callback); } -ProblemImpl* CreateGradientCheckingProblemImpl( +std::unique_ptr<ProblemImpl> CreateGradientCheckingProblemImpl( ProblemImpl* problem_impl, double relative_step_size, double relative_precision, GradientCheckingIterationCallback* callback) { CHECK(callback != nullptr); - // We create new CostFunctions by wrapping the original CostFunction - // in a gradient checking CostFunction. So its okay for the - // ProblemImpl to take ownership of it and destroy it. The - // LossFunctions and LocalParameterizations are reused and since - // they are owned by problem_impl, gradient_checking_problem_impl + // We create new CostFunctions by wrapping the original CostFunction in a + // gradient checking CostFunction. So its okay for the ProblemImpl to take + // ownership of it and destroy it. The LossFunctions and Manifolds are reused + // and since they are owned by problem_impl, gradient_checking_problem_impl // should not take ownership of it. Problem::Options gradient_checking_problem_options; gradient_checking_problem_options.cost_function_ownership = TAKE_OWNERSHIP; gradient_checking_problem_options.loss_function_ownership = DO_NOT_TAKE_OWNERSHIP; - gradient_checking_problem_options.local_parameterization_ownership = - DO_NOT_TAKE_OWNERSHIP; + gradient_checking_problem_options.manifold_ownership = DO_NOT_TAKE_OWNERSHIP; gradient_checking_problem_options.context = problem_impl->context(); NumericDiffOptions numeric_diff_options; numeric_diff_options.relative_step_size = relative_step_size; - ProblemImpl* gradient_checking_problem_impl = - new ProblemImpl(gradient_checking_problem_options); + auto gradient_checking_problem_impl = + std::make_unique<ProblemImpl>(gradient_checking_problem_options); Program* program = problem_impl->mutable_program(); - // For every ParameterBlock in problem_impl, create a new parameter - // block with the same local parameterization and constancy. + // For every ParameterBlock in problem_impl, create a new parameter block with + // the same manifold and constancy. const vector<ParameterBlock*>& parameter_blocks = program->parameter_blocks(); - for (int i = 0; i < parameter_blocks.size(); ++i) { - ParameterBlock* parameter_block = parameter_blocks[i]; + for (auto* parameter_block : parameter_blocks) { gradient_checking_problem_impl->AddParameterBlock( parameter_block->mutable_user_state(), parameter_block->Size(), - parameter_block->mutable_local_parameterization()); + parameter_block->mutable_manifold()); if (parameter_block->IsConstant()) { gradient_checking_problem_impl->SetParameterBlockConstant( @@ -238,22 +235,22 @@ ProblemImpl* CreateGradientCheckingProblemImpl( string extra_info = StringPrintf("Residual block id %d; depends on parameters [", i); vector<double*> parameter_blocks; - vector<const LocalParameterization*> local_parameterizations; + vector<const Manifold*> manifolds; parameter_blocks.reserve(residual_block->NumParameterBlocks()); - local_parameterizations.reserve(residual_block->NumParameterBlocks()); + manifolds.reserve(residual_block->NumParameterBlocks()); for (int j = 0; j < residual_block->NumParameterBlocks(); ++j) { ParameterBlock* parameter_block = residual_block->parameter_blocks()[j]; parameter_blocks.push_back(parameter_block->mutable_user_state()); StringAppendF(&extra_info, "%p", parameter_block->mutable_user_state()); extra_info += (j < residual_block->NumParameterBlocks() - 1) ? ", " : "]"; - local_parameterizations.push_back(problem_impl->GetParameterization( - parameter_block->mutable_user_state())); + manifolds.push_back( + problem_impl->GetManifold(parameter_block->mutable_user_state())); } // Wrap the original CostFunction in a GradientCheckingCostFunction. CostFunction* gradient_checking_cost_function = new GradientCheckingCostFunction(residual_block->cost_function(), - &local_parameterizations, + &manifolds, numeric_diff_options, relative_precision, extra_info, diff --git a/extern/ceres/internal/ceres/gradient_checking_cost_function.h b/extern/ceres/internal/ceres/gradient_checking_cost_function.h index ea6e9b31c8c..0caafafa8fa 100644 --- a/extern/ceres/internal/ceres/gradient_checking_cost_function.h +++ b/extern/ceres/internal/ceres/gradient_checking_cost_function.h @@ -32,13 +32,15 @@ #ifndef CERES_INTERNAL_GRADIENT_CHECKING_COST_FUNCTION_H_ #define CERES_INTERNAL_GRADIENT_CHECKING_COST_FUNCTION_H_ +#include <memory> #include <mutex> #include <string> #include "ceres/cost_function.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/iteration_callback.h" -#include "ceres/local_parameterization.h" +#include "ceres/manifold.h" namespace ceres { namespace internal { @@ -47,7 +49,7 @@ class ProblemImpl; // Callback that collects information about gradient checking errors, and // will abort the solve as soon as an error occurs. -class CERES_EXPORT_INTERNAL GradientCheckingIterationCallback +class CERES_NO_EXPORT GradientCheckingIterationCallback : public IterationCallback { public: GradientCheckingIterationCallback(); @@ -73,9 +75,10 @@ class CERES_EXPORT_INTERNAL GradientCheckingIterationCallback // with finite differences. This API is only intended for unit tests that intend // to check the functionality of the GradientCheckingCostFunction // implementation directly. -CERES_EXPORT_INTERNAL CostFunction* CreateGradientCheckingCostFunction( +CERES_NO_EXPORT std::unique_ptr<CostFunction> +CreateGradientCheckingCostFunction( const CostFunction* cost_function, - const std::vector<const LocalParameterization*>* local_parameterizations, + const std::vector<const Manifold*>* manifolds, double relative_step_size, double relative_precision, const std::string& extra_info, @@ -92,8 +95,6 @@ CERES_EXPORT_INTERNAL CostFunction* CreateGradientCheckingCostFunction( // iteration, the respective cost function will notify the // GradientCheckingIterationCallback. // -// The caller owns the returned ProblemImpl object. -// // Note: This is quite inefficient and is intended only for debugging. // // relative_step_size and relative_precision are parameters to control @@ -102,7 +103,7 @@ CERES_EXPORT_INTERNAL CostFunction* CreateGradientCheckingCostFunction( // jacobians obtained by numerically differentiating them. See the // documentation of 'numeric_derivative_relative_step_size' in solver.h for a // better explanation. -CERES_EXPORT_INTERNAL ProblemImpl* CreateGradientCheckingProblemImpl( +CERES_NO_EXPORT std::unique_ptr<ProblemImpl> CreateGradientCheckingProblemImpl( ProblemImpl* problem_impl, double relative_step_size, double relative_precision, @@ -111,4 +112,6 @@ CERES_EXPORT_INTERNAL ProblemImpl* CreateGradientCheckingProblemImpl( } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_GRADIENT_CHECKING_COST_FUNCTION_H_ diff --git a/extern/ceres/internal/ceres/gradient_problem.cc b/extern/ceres/internal/ceres/gradient_problem.cc index ba33fbc90f8..cdd472fe87f 100644 --- a/extern/ceres/internal/ceres/gradient_problem.cc +++ b/extern/ceres/internal/ceres/gradient_problem.cc @@ -30,49 +30,75 @@ #include "ceres/gradient_problem.h" +#include <memory> + #include "ceres/local_parameterization.h" +#include "ceres/manifold_adapter.h" #include "glog/logging.h" namespace ceres { GradientProblem::GradientProblem(FirstOrderFunction* function) : function_(function), - parameterization_( - new IdentityParameterization(function_->NumParameters())), - scratch_(new double[function_->NumParameters()]) {} + manifold_(std::make_unique<EuclideanManifold<DYNAMIC>>( + function_->NumParameters())), + scratch_(new double[function_->NumParameters()]) { + CHECK(function != nullptr); +} GradientProblem::GradientProblem(FirstOrderFunction* function, LocalParameterization* parameterization) : function_(function), parameterization_(parameterization), scratch_(new double[function_->NumParameters()]) { - CHECK_EQ(function_->NumParameters(), parameterization_->GlobalSize()); + CHECK(function != nullptr); + if (parameterization != nullptr) { + manifold_ = + std::make_unique<internal::ManifoldAdapter>(parameterization_.get()); + } else { + manifold_ = std::make_unique<EuclideanManifold<DYNAMIC>>( + function_->NumParameters()); + } + CHECK_EQ(function_->NumParameters(), manifold_->AmbientSize()); +} + +GradientProblem::GradientProblem(FirstOrderFunction* function, + Manifold* manifold) + : function_(function), scratch_(new double[function_->NumParameters()]) { + CHECK(function != nullptr); + if (manifold != nullptr) { + manifold_.reset(manifold); + } else { + manifold_ = std::make_unique<EuclideanManifold<DYNAMIC>>( + function_->NumParameters()); + } + CHECK_EQ(function_->NumParameters(), manifold_->AmbientSize()); } int GradientProblem::NumParameters() const { return function_->NumParameters(); } -int GradientProblem::NumLocalParameters() const { - return parameterization_->LocalSize(); +int GradientProblem::NumTangentParameters() const { + return manifold_->TangentSize(); } bool GradientProblem::Evaluate(const double* parameters, double* cost, double* gradient) const { - if (gradient == NULL) { - return function_->Evaluate(parameters, cost, NULL); + if (gradient == nullptr) { + return function_->Evaluate(parameters, cost, nullptr); } return (function_->Evaluate(parameters, cost, scratch_.get()) && - parameterization_->MultiplyByJacobian( + manifold_->RightMultiplyByPlusJacobian( parameters, 1, scratch_.get(), gradient)); } bool GradientProblem::Plus(const double* x, const double* delta, double* x_plus_delta) const { - return parameterization_->Plus(x, delta, x_plus_delta); + return manifold_->Plus(x, delta, x_plus_delta); } } // namespace ceres diff --git a/extern/ceres/internal/ceres/gradient_problem_evaluator.h b/extern/ceres/internal/ceres/gradient_problem_evaluator.h index d224dbed0ae..efbb257ec75 100644 --- a/extern/ceres/internal/ceres/gradient_problem_evaluator.h +++ b/extern/ceres/internal/ceres/gradient_problem_evaluator.h @@ -32,30 +32,34 @@ #define CERES_INTERNAL_GRADIENT_PROBLEM_EVALUATOR_H_ #include <map> +#include <memory> #include <string> #include "ceres/evaluator.h" #include "ceres/execution_summary.h" #include "ceres/gradient_problem.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" +#include "ceres/sparse_matrix.h" #include "ceres/wall_time.h" namespace ceres { namespace internal { -class GradientProblemEvaluator : public Evaluator { +class CERES_NO_EXPORT GradientProblemEvaluator final : public Evaluator { public: explicit GradientProblemEvaluator(const GradientProblem& problem) : problem_(problem) {} - virtual ~GradientProblemEvaluator() {} - SparseMatrix* CreateJacobian() const final { return nullptr; } + + std::unique_ptr<SparseMatrix> CreateJacobian() const final { return nullptr; } + bool Evaluate(const EvaluateOptions& evaluate_options, const double* state, double* cost, double* residuals, double* gradient, SparseMatrix* jacobian) final { - CHECK(jacobian == NULL); + CHECK(jacobian == nullptr); ScopedExecutionTimer total_timer("Evaluator::Total", &execution_summary_); // The reason we use Residual and Jacobian here even when we are // only computing the cost and gradient has to do with the fact @@ -65,7 +69,7 @@ class GradientProblemEvaluator : public Evaluator { // to be consistent across the code base for the time accounting // to work. ScopedExecutionTimer call_type_timer( - gradient == NULL ? "Evaluator::Residual" : "Evaluator::Jacobian", + gradient == nullptr ? "Evaluator::Residual" : "Evaluator::Jacobian", &execution_summary_); return problem_.Evaluate(state, cost, gradient); } @@ -96,4 +100,6 @@ class GradientProblemEvaluator : public Evaluator { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_GRADIENT_PROBLEM_EVALUATOR_H_ diff --git a/extern/ceres/internal/ceres/gradient_problem_solver.cc b/extern/ceres/internal/ceres/gradient_problem_solver.cc index b72fad91542..9382556d292 100644 --- a/extern/ceres/internal/ceres/gradient_problem_solver.cc +++ b/extern/ceres/internal/ceres/gradient_problem_solver.cc @@ -36,7 +36,7 @@ #include "ceres/gradient_problem.h" #include "ceres/gradient_problem_evaluator.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/map_util.h" #include "ceres/minimizer.h" #include "ceres/solver.h" @@ -92,7 +92,7 @@ bool GradientProblemSolver::Options::IsValid(std::string* error) const { return solver_options.IsValid(error); } -GradientProblemSolver::~GradientProblemSolver() {} +GradientProblemSolver::~GradientProblemSolver() = default; void GradientProblemSolver::Solve(const GradientProblemSolver::Options& options, const GradientProblem& problem, @@ -135,21 +135,22 @@ void GradientProblemSolver::Solve(const GradientProblemSolver::Options& options, // now. Minimizer::Options minimizer_options = Minimizer::Options(GradientProblemSolverOptionsToSolverOptions(options)); - minimizer_options.evaluator.reset(new GradientProblemEvaluator(problem)); + minimizer_options.evaluator = + std::make_unique<GradientProblemEvaluator>(problem); std::unique_ptr<IterationCallback> logging_callback; if (options.logging_type != SILENT) { - logging_callback.reset( - new LoggingCallback(LINE_SEARCH, options.minimizer_progress_to_stdout)); + logging_callback = std::make_unique<LoggingCallback>( + LINE_SEARCH, options.minimizer_progress_to_stdout); minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(), logging_callback.get()); } std::unique_ptr<IterationCallback> state_updating_callback; if (options.update_state_every_iteration) { - state_updating_callback.reset( - new GradientProblemSolverStateUpdatingCallback( - problem.NumParameters(), solution.data(), parameters_ptr)); + state_updating_callback = + std::make_unique<GradientProblemSolverStateUpdatingCallback>( + problem.NumParameters(), solution.data(), parameters_ptr); minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(), state_updating_callback.get()); } diff --git a/extern/ceres/internal/ceres/graph.h b/extern/ceres/internal/ceres/graph.h index 9b26158753f..6a6f8f01c00 100644 --- a/extern/ceres/internal/ceres/graph.h +++ b/extern/ceres/internal/ceres/graph.h @@ -36,6 +36,7 @@ #include <unordered_set> #include <utility> +#include "ceres/internal/export.h" #include "ceres/map_util.h" #include "ceres/pair_hash.h" #include "ceres/types.h" @@ -47,10 +48,8 @@ namespace internal { // A unweighted undirected graph templated over the vertex ids. Vertex // should be hashable. template <typename Vertex> -class Graph { +class CERES_NO_EXPORT Graph { public: - Graph() {} - // Add a vertex. void AddVertex(const Vertex& vertex) { if (vertices_.insert(vertex).second) { @@ -106,8 +105,6 @@ class Graph { template <typename Vertex> class WeightedGraph { public: - WeightedGraph() {} - // Add a weighted vertex. If the vertex already exists in the graph, // its weight is set to the new weight. void AddVertex(const Vertex& vertex, double weight) { diff --git a/extern/ceres/internal/ceres/graph_algorithms.h b/extern/ceres/internal/ceres/graph_algorithms.h index 7d63b337f68..5299f80d963 100644 --- a/extern/ceres/internal/ceres/graph_algorithms.h +++ b/extern/ceres/internal/ceres/graph_algorithms.h @@ -34,12 +34,14 @@ #define CERES_INTERNAL_GRAPH_ALGORITHMS_H_ #include <algorithm> +#include <memory> #include <unordered_map> #include <unordered_set> #include <utility> #include <vector> #include "ceres/graph.h" +#include "ceres/internal/export.h" #include "ceres/wall_time.h" #include "glog/logging.h" @@ -49,7 +51,7 @@ namespace internal { // Compare two vertices of a graph by their degrees, if the degrees // are equal then order them by their ids. template <typename Vertex> -class VertexTotalOrdering { +class CERES_NO_EXPORT VertexTotalOrdering { public: explicit VertexTotalOrdering(const Graph<Vertex>& graph) : graph_(graph) {} @@ -257,11 +259,11 @@ Vertex FindConnectedComponent(const Vertex& vertex, // spanning forest, or a collection of linear paths that span the // graph G. template <typename Vertex> -WeightedGraph<Vertex>* Degree2MaximumSpanningForest( +std::unique_ptr<WeightedGraph<Vertex>> Degree2MaximumSpanningForest( const WeightedGraph<Vertex>& graph) { // Array of edges sorted in decreasing order of their weights. std::vector<std::pair<double, std::pair<Vertex, Vertex>>> weighted_edges; - WeightedGraph<Vertex>* forest = new WeightedGraph<Vertex>(); + auto forest = std::make_unique<WeightedGraph<Vertex>>(); // Disjoint-set to keep track of the connected components in the // maximum spanning tree. diff --git a/extern/ceres/internal/ceres/implicit_schur_complement.cc b/extern/ceres/internal/ceres/implicit_schur_complement.cc index f2196d4ef9c..677d767fa93 100644 --- a/extern/ceres/internal/ceres/implicit_schur_complement.cc +++ b/extern/ceres/internal/ceres/implicit_schur_complement.cc @@ -43,17 +43,15 @@ namespace internal { ImplicitSchurComplement::ImplicitSchurComplement( const LinearSolver::Options& options) - : options_(options), D_(NULL), b_(NULL) {} - -ImplicitSchurComplement::~ImplicitSchurComplement() {} + : options_(options), D_(nullptr), b_(nullptr) {} void ImplicitSchurComplement::Init(const BlockSparseMatrix& A, const double* D, const double* b) { // Since initialization is reasonably heavy, perhaps we can save on // constructing a new object everytime. - if (A_ == NULL) { - A_.reset(PartitionedMatrixViewBase::Create(options_, A)); + if (A_ == nullptr) { + A_ = PartitionedMatrixViewBase::Create(options_, A); } D_ = D; @@ -61,10 +59,10 @@ void ImplicitSchurComplement::Init(const BlockSparseMatrix& A, // Initialize temporary storage and compute the block diagonals of // E'E and F'E. - if (block_diagonal_EtE_inverse_ == NULL) { - block_diagonal_EtE_inverse_.reset(A_->CreateBlockDiagonalEtE()); + if (block_diagonal_EtE_inverse_ == nullptr) { + block_diagonal_EtE_inverse_ = A_->CreateBlockDiagonalEtE(); if (options_.preconditioner_type == JACOBI) { - block_diagonal_FtF_inverse_.reset(A_->CreateBlockDiagonalFtF()); + block_diagonal_FtF_inverse_ = A_->CreateBlockDiagonalFtF(); } rhs_.resize(A_->num_cols_f()); rhs_.setZero(); @@ -84,7 +82,7 @@ void ImplicitSchurComplement::Init(const BlockSparseMatrix& A, // the block diagonals and invert them. AddDiagonalAndInvert(D_, block_diagonal_EtE_inverse_.get()); if (options_.preconditioner_type == JACOBI) { - AddDiagonalAndInvert((D_ == NULL) ? NULL : D_ + A_->num_cols_e(), + AddDiagonalAndInvert((D_ == nullptr) ? nullptr : D_ + A_->num_cols_e(), block_diagonal_FtF_inverse_.get()); } @@ -118,7 +116,7 @@ void ImplicitSchurComplement::RightMultiply(const double* x, double* y) const { A_->RightMultiplyE(tmp_e_cols_2_.data(), tmp_rows_.data()); // y5 = D * x - if (D_ != NULL) { + if (D_ != nullptr) { ConstVectorRef Dref(D_ + A_->num_cols_e(), num_cols()); VectorRef(y, num_cols()) = (Dref.array().square() * ConstVectorRef(x, num_cols()).array()) @@ -138,15 +136,15 @@ void ImplicitSchurComplement::AddDiagonalAndInvert( const double* D, BlockSparseMatrix* block_diagonal) { const CompressedRowBlockStructure* block_diagonal_structure = block_diagonal->block_structure(); - for (int r = 0; r < block_diagonal_structure->rows.size(); ++r) { - const int row_block_pos = block_diagonal_structure->rows[r].block.position; - const int row_block_size = block_diagonal_structure->rows[r].block.size; - const Cell& cell = block_diagonal_structure->rows[r].cells[0]; + for (const auto& row : block_diagonal_structure->rows) { + const int row_block_pos = row.block.position; + const int row_block_size = row.block.size; + const Cell& cell = row.cells[0]; MatrixRef m(block_diagonal->mutable_values() + cell.position, row_block_size, row_block_size); - if (D != NULL) { + if (D != nullptr) { ConstVectorRef d(D + row_block_pos, row_block_size); m += d.array().square().matrix().asDiagonal(); } diff --git a/extern/ceres/internal/ceres/implicit_schur_complement.h b/extern/ceres/internal/ceres/implicit_schur_complement.h index e83892af017..598d48411aa 100644 --- a/extern/ceres/internal/ceres/implicit_schur_complement.h +++ b/extern/ceres/internal/ceres/implicit_schur_complement.h @@ -36,8 +36,9 @@ #include <memory> +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/linear_operator.h" #include "ceres/linear_solver.h" #include "ceres/partitioned_matrix_view.h" @@ -88,7 +89,7 @@ class BlockSparseMatrix; // RightMultiply (and the LeftMultiply) methods are not thread safe as // they depend on mutable arrays used for the temporaries needed to // compute the product y += Sx; -class CERES_EXPORT_INTERNAL ImplicitSchurComplement : public LinearOperator { +class CERES_NO_EXPORT ImplicitSchurComplement final : public LinearOperator { public: // num_eliminate_blocks is the number of E blocks in the matrix // A. @@ -100,7 +101,6 @@ class CERES_EXPORT_INTERNAL ImplicitSchurComplement : public LinearOperator { // TODO(sameeragarwal): Get rid of the two bools below and replace // them with enums. explicit ImplicitSchurComplement(const LinearSolver::Options& options); - virtual ~ImplicitSchurComplement(); // Initialize the Schur complement for a linear least squares // problem of the form @@ -166,4 +166,6 @@ class CERES_EXPORT_INTERNAL ImplicitSchurComplement : public LinearOperator { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_IMPLICIT_SCHUR_COMPLEMENT_H_ diff --git a/extern/ceres/internal/ceres/inner_product_computer.cc b/extern/ceres/internal/ceres/inner_product_computer.cc index ef38b7b3ad4..fbc43bfed8b 100644 --- a/extern/ceres/internal/ceres/inner_product_computer.cc +++ b/extern/ceres/internal/ceres/inner_product_computer.cc @@ -31,6 +31,7 @@ #include "ceres/inner_product_computer.h" #include <algorithm> +#include <memory> #include "ceres/small_blas.h" @@ -44,11 +45,12 @@ namespace internal { // or the lower triangular part of the product. // // num_nonzeros is the number of non-zeros in the result matrix. -CompressedRowSparseMatrix* InnerProductComputer::CreateResultMatrix( +std::unique_ptr<CompressedRowSparseMatrix> +InnerProductComputer::CreateResultMatrix( const CompressedRowSparseMatrix::StorageType storage_type, const int num_nonzeros) { - CompressedRowSparseMatrix* matrix = - new CompressedRowSparseMatrix(m_.num_cols(), m_.num_cols(), num_nonzeros); + auto matrix = std::make_unique<CompressedRowSparseMatrix>( + m_.num_cols(), m_.num_cols(), num_nonzeros); matrix->set_storage_type(storage_type); const CompressedRowBlockStructure* bs = m_.block_structure(); @@ -116,14 +118,14 @@ InnerProductComputer::InnerProductComputer(const BlockSparseMatrix& m, // // product_storage_type controls the form of the output matrix. It // can be LOWER_TRIANGULAR or UPPER_TRIANGULAR. -InnerProductComputer* InnerProductComputer::Create( +std::unique_ptr<InnerProductComputer> InnerProductComputer::Create( const BlockSparseMatrix& m, CompressedRowSparseMatrix::StorageType product_storage_type) { return InnerProductComputer::Create( m, 0, m.block_structure()->rows.size(), product_storage_type); } -InnerProductComputer* InnerProductComputer::Create( +std::unique_ptr<InnerProductComputer> InnerProductComputer::Create( const BlockSparseMatrix& m, const int start_row_block, const int end_row_block, @@ -132,8 +134,8 @@ InnerProductComputer* InnerProductComputer::Create( product_storage_type == CompressedRowSparseMatrix::UPPER_TRIANGULAR); CHECK_GT(m.num_nonzeros(), 0) << "Congratulations, you found a bug in Ceres. Please report it."; - InnerProductComputer* inner_product_computer = - new InnerProductComputer(m, start_row_block, end_row_block); + std::unique_ptr<InnerProductComputer> inner_product_computer( + new InnerProductComputer(m, start_row_block, end_row_block)); inner_product_computer->Init(product_storage_type); return inner_product_computer; } @@ -165,8 +167,8 @@ void InnerProductComputer::Init( for (int c2 = c2_begin; c2 < c2_end; ++c2) { const Cell& cell2 = row.cells[c2]; - product_terms.push_back(InnerProductComputer::ProductTerm( - cell1.block_id, cell2.block_id, product_terms.size())); + product_terms.emplace_back( + cell1.block_id, cell2.block_id, product_terms.size()); } } } @@ -183,7 +185,7 @@ void InnerProductComputer::ComputeOffsetsAndCreateResultMatrix( std::vector<int> row_block_nnz; const int num_nonzeros = ComputeNonzeros(product_terms, &row_block_nnz); - result_.reset(CreateResultMatrix(product_storage_type, num_nonzeros)); + result_ = CreateResultMatrix(product_storage_type, num_nonzeros); // Populate the row non-zero counts in the result matrix. int* crsm_rows = result_->mutable_rows(); diff --git a/extern/ceres/internal/ceres/inner_product_computer.h b/extern/ceres/internal/ceres/inner_product_computer.h index 04ec1d18316..c6ed0b23e87 100644 --- a/extern/ceres/internal/ceres/inner_product_computer.h +++ b/extern/ceres/internal/ceres/inner_product_computer.h @@ -36,7 +36,8 @@ #include "ceres/block_sparse_matrix.h" #include "ceres/compressed_row_sparse_matrix.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -61,7 +62,7 @@ namespace internal { // This is not a problem as sparse linear algebra libraries can ignore // these entries with ease and the space used is minimal/linear in the // size of the matrices. -class CERES_EXPORT_INTERNAL InnerProductComputer { +class CERES_NO_EXPORT InnerProductComputer { public: // Factory // @@ -74,7 +75,7 @@ class CERES_EXPORT_INTERNAL InnerProductComputer { // // The user must ensure that the matrix m is valid for the life time // of this object. - static InnerProductComputer* Create( + static std::unique_ptr<InnerProductComputer> Create( const BlockSparseMatrix& m, CompressedRowSparseMatrix::StorageType storage_type); @@ -83,7 +84,7 @@ class CERES_EXPORT_INTERNAL InnerProductComputer { // // a = m(start_row_block : end_row_block, :); // result = a' * a; - static InnerProductComputer* Create( + static std::unique_ptr<InnerProductComputer> Create( const BlockSparseMatrix& m, int start_row_block, int end_row_block, @@ -127,7 +128,7 @@ class CERES_EXPORT_INTERNAL InnerProductComputer { void Init(CompressedRowSparseMatrix::StorageType storage_type); - CompressedRowSparseMatrix* CreateResultMatrix( + std::unique_ptr<CompressedRowSparseMatrix> CreateResultMatrix( const CompressedRowSparseMatrix::StorageType storage_type, int num_nonzeros); @@ -155,4 +156,6 @@ class CERES_EXPORT_INTERNAL InnerProductComputer { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_INNER_PRODUCT_COMPUTER_H_ diff --git a/extern/ceres/internal/ceres/is_close.h b/extern/ceres/internal/ceres/is_close.h index b781a4493ff..a1e4e2f6721 100644 --- a/extern/ceres/internal/ceres/is_close.h +++ b/extern/ceres/internal/ceres/is_close.h @@ -33,21 +33,24 @@ #ifndef CERES_INTERNAL_IS_CLOSE_H_ #define CERES_INTERNAL_IS_CLOSE_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { // Returns true if x and y have a relative (unsigned) difference less than // relative_precision and false otherwise. Stores the relative and absolute -// difference in relative/absolute_error if non-NULL. If one of the two values -// is exactly zero, the absolute difference will be compared, and relative_error -// will be set to the absolute difference. -CERES_EXPORT_INTERNAL bool IsClose(double x, - double y, - double relative_precision, - double* relative_error, - double* absolute_error); +// difference in relative/absolute_error if non-nullptr. If one of the two +// values is exactly zero, the absolute difference will be compared, and +// relative_error will be set to the absolute difference. +CERES_NO_EXPORT bool IsClose(double x, + double y, + double relative_precision, + double* relative_error, + double* absolute_error); } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_IS_CLOSE_H_ diff --git a/extern/ceres/internal/ceres/iteration_callback.cc b/extern/ceres/internal/ceres/iteration_callback.cc new file mode 100644 index 00000000000..804811d2807 --- /dev/null +++ b/extern/ceres/internal/ceres/iteration_callback.cc @@ -0,0 +1,37 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2019 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#include "ceres/iteration_callback.h" + +namespace ceres { + +IterationCallback::~IterationCallback() = default; + +} // namespace ceres diff --git a/extern/ceres/internal/ceres/iterative_refiner.cc b/extern/ceres/internal/ceres/iterative_refiner.cc index 5f0bfdd250d..18154690597 100644 --- a/extern/ceres/internal/ceres/iterative_refiner.cc +++ b/extern/ceres/internal/ceres/iterative_refiner.cc @@ -42,7 +42,7 @@ namespace internal { IterativeRefiner::IterativeRefiner(const int max_num_iterations) : max_num_iterations_(max_num_iterations) {} -IterativeRefiner::~IterativeRefiner() {} +IterativeRefiner::~IterativeRefiner() = default; void IterativeRefiner::Allocate(int num_cols) { residual_.resize(num_cols); diff --git a/extern/ceres/internal/ceres/iterative_refiner.h b/extern/ceres/internal/ceres/iterative_refiner.h index 08f8d6762cf..837af178ab4 100644 --- a/extern/ceres/internal/ceres/iterative_refiner.h +++ b/extern/ceres/internal/ceres/iterative_refiner.h @@ -33,10 +33,11 @@ // This include must come before any #ifndef check on Ceres compile options. // clang-format off -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" // clang-format on #include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -57,11 +58,11 @@ class SparseMatrix; // Definite linear systems. // // The above iterative loop is run until max_num_iterations is reached. -class CERES_EXPORT_INTERNAL IterativeRefiner { +class CERES_NO_EXPORT IterativeRefiner { public: // max_num_iterations is the number of refinement iterations to // perform. - IterativeRefiner(int max_num_iterations); + explicit IterativeRefiner(int max_num_iterations); // Needed for mocking. virtual ~IterativeRefiner(); diff --git a/extern/ceres/internal/ceres/iterative_schur_complement_solver.cc b/extern/ceres/internal/ceres/iterative_schur_complement_solver.cc index 143df5e5814..bc22d68bc55 100644 --- a/extern/ceres/internal/ceres/iterative_schur_complement_solver.cc +++ b/extern/ceres/internal/ceres/iterative_schur_complement_solver.cc @@ -32,6 +32,7 @@ #include <algorithm> #include <cstring> +#include <utility> #include <vector> #include "Eigen/Dense" @@ -54,10 +55,10 @@ namespace ceres { namespace internal { IterativeSchurComplementSolver::IterativeSchurComplementSolver( - const LinearSolver::Options& options) - : options_(options) {} + LinearSolver::Options options) + : options_(std::move(options)) {} -IterativeSchurComplementSolver::~IterativeSchurComplementSolver() {} +IterativeSchurComplementSolver::~IterativeSchurComplementSolver() = default; LinearSolver::Summary IterativeSchurComplementSolver::SolveImpl( BlockSparseMatrix* A, @@ -69,13 +70,13 @@ LinearSolver::Summary IterativeSchurComplementSolver::SolveImpl( CHECK(A->block_structure() != nullptr); const int num_eliminate_blocks = options_.elimination_groups[0]; // Initialize a ImplicitSchurComplement object. - if (schur_complement_ == NULL) { + if (schur_complement_ == nullptr) { DetectStructure(*(A->block_structure()), num_eliminate_blocks, &options_.row_block_size, &options_.e_block_size, &options_.f_block_size); - schur_complement_.reset(new ImplicitSchurComplement(options_)); + schur_complement_ = std::make_unique<ImplicitSchurComplement>(options_); } schur_complement_->Init(*A, per_solve_options.D, b); @@ -86,7 +87,7 @@ LinearSolver::Summary IterativeSchurComplementSolver::SolveImpl( LinearSolver::Summary summary; summary.num_iterations = 0; summary.termination_type = LINEAR_SOLVER_SUCCESS; - schur_complement_->BackSubstitute(NULL, x); + schur_complement_->BackSubstitute(nullptr, x); return summary; } @@ -104,7 +105,7 @@ LinearSolver::Summary IterativeSchurComplementSolver::SolveImpl( cg_per_solve_options.q_tolerance = per_solve_options.q_tolerance; CreatePreconditioner(A); - if (preconditioner_.get() != NULL) { + if (preconditioner_.get() != nullptr) { if (!preconditioner_->Update(*A, per_solve_options.D)) { LinearSolver::Summary summary; summary.num_iterations = 0; @@ -134,7 +135,7 @@ LinearSolver::Summary IterativeSchurComplementSolver::SolveImpl( void IterativeSchurComplementSolver::CreatePreconditioner( BlockSparseMatrix* A) { if (options_.preconditioner_type == IDENTITY || - preconditioner_.get() != NULL) { + preconditioner_.get() != nullptr) { return; } @@ -149,22 +150,22 @@ void IterativeSchurComplementSolver::CreatePreconditioner( preconditioner_options.e_block_size = options_.e_block_size; preconditioner_options.f_block_size = options_.f_block_size; preconditioner_options.elimination_groups = options_.elimination_groups; - CHECK(options_.context != NULL); + CHECK(options_.context != nullptr); preconditioner_options.context = options_.context; switch (options_.preconditioner_type) { case JACOBI: - preconditioner_.reset(new SparseMatrixPreconditionerWrapper( - schur_complement_->block_diagonal_FtF_inverse())); + preconditioner_ = std::make_unique<SparseMatrixPreconditionerWrapper>( + schur_complement_->block_diagonal_FtF_inverse()); break; case SCHUR_JACOBI: - preconditioner_.reset(new SchurJacobiPreconditioner( - *A->block_structure(), preconditioner_options)); + preconditioner_ = std::make_unique<SchurJacobiPreconditioner>( + *A->block_structure(), preconditioner_options); break; case CLUSTER_JACOBI: case CLUSTER_TRIDIAGONAL: - preconditioner_.reset(new VisibilityBasedPreconditioner( - *A->block_structure(), preconditioner_options)); + preconditioner_ = std::make_unique<VisibilityBasedPreconditioner>( + *A->block_structure(), preconditioner_options); break; default: LOG(FATAL) << "Unknown Preconditioner Type"; diff --git a/extern/ceres/internal/ceres/iterative_schur_complement_solver.h b/extern/ceres/internal/ceres/iterative_schur_complement_solver.h index 37606b32d3a..50f469484f8 100644 --- a/extern/ceres/internal/ceres/iterative_schur_complement_solver.h +++ b/extern/ceres/internal/ceres/iterative_schur_complement_solver.h @@ -33,8 +33,9 @@ #include <memory> +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" #include "ceres/types.h" @@ -69,15 +70,15 @@ class Preconditioner; // a proof of this fact and others related to this solver please see // the section on Domain Decomposition Methods in Saad's book // "Iterative Methods for Sparse Linear Systems". -class CERES_EXPORT_INTERNAL IterativeSchurComplementSolver +class CERES_NO_EXPORT IterativeSchurComplementSolver final : public BlockSparseMatrixSolver { public: - explicit IterativeSchurComplementSolver(const LinearSolver::Options& options); + explicit IterativeSchurComplementSolver(LinearSolver::Options options); IterativeSchurComplementSolver(const IterativeSchurComplementSolver&) = delete; void operator=(const IterativeSchurComplementSolver&) = delete; - virtual ~IterativeSchurComplementSolver(); + ~IterativeSchurComplementSolver() override; private: LinearSolver::Summary SolveImpl(BlockSparseMatrix* A, @@ -96,4 +97,6 @@ class CERES_EXPORT_INTERNAL IterativeSchurComplementSolver } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_ITERATIVE_SCHUR_COMPLEMENT_SOLVER_H_ diff --git a/extern/ceres/internal/ceres/lapack.cc b/extern/ceres/internal/ceres/lapack.cc deleted file mode 100644 index a159ec70696..00000000000 --- a/extern/ceres/internal/ceres/lapack.cc +++ /dev/null @@ -1,190 +0,0 @@ -// Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. -// http://ceres-solver.org/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Author: sameeragarwal@google.com (Sameer Agarwal) - -#include "ceres/lapack.h" - -#include "ceres/internal/port.h" -#include "ceres/linear_solver.h" -#include "glog/logging.h" - -#ifndef CERES_NO_LAPACK -// C interface to the LAPACK Cholesky factorization and triangular solve. -extern "C" void dpotrf_(char* uplo, int* n, double* a, int* lda, int* info); - -extern "C" void dpotrs_(char* uplo, - int* n, - int* nrhs, - double* a, - int* lda, - double* b, - int* ldb, - int* info); - -extern "C" void dgels_(char* uplo, - int* m, - int* n, - int* nrhs, - double* a, - int* lda, - double* b, - int* ldb, - double* work, - int* lwork, - int* info); -#endif - -namespace ceres { -namespace internal { - -LinearSolverTerminationType LAPACK::SolveInPlaceUsingCholesky( - int num_rows, - const double* in_lhs, - double* rhs_and_solution, - std::string* message) { -#ifdef CERES_NO_LAPACK - LOG(FATAL) << "Ceres was built without a BLAS library."; - return LINEAR_SOLVER_FATAL_ERROR; -#else - char uplo = 'L'; - int n = num_rows; - int info = 0; - int nrhs = 1; - double* lhs = const_cast<double*>(in_lhs); - - dpotrf_(&uplo, &n, lhs, &n, &info); - if (info < 0) { - LOG(FATAL) << "Congratulations, you found a bug in Ceres." - << "Please report it." - << "LAPACK::dpotrf fatal error." - << "Argument: " << -info << " is invalid."; - return LINEAR_SOLVER_FATAL_ERROR; - } - - if (info > 0) { - *message = StringPrintf( - "LAPACK::dpotrf numerical failure. " - "The leading minor of order %d is not positive definite.", - info); - return LINEAR_SOLVER_FAILURE; - } - - dpotrs_(&uplo, &n, &nrhs, lhs, &n, rhs_and_solution, &n, &info); - if (info < 0) { - LOG(FATAL) << "Congratulations, you found a bug in Ceres." - << "Please report it." - << "LAPACK::dpotrs fatal error." - << "Argument: " << -info << " is invalid."; - return LINEAR_SOLVER_FATAL_ERROR; - } - - *message = "Success"; - return LINEAR_SOLVER_SUCCESS; -#endif -} - -int LAPACK::EstimateWorkSizeForQR(int num_rows, int num_cols) { -#ifdef CERES_NO_LAPACK - LOG(FATAL) << "Ceres was built without a LAPACK library."; - return -1; -#else - char trans = 'N'; - int nrhs = 1; - int lwork = -1; - double work; - int info = 0; - dgels_(&trans, - &num_rows, - &num_cols, - &nrhs, - NULL, - &num_rows, - NULL, - &num_rows, - &work, - &lwork, - &info); - - if (info < 0) { - LOG(FATAL) << "Congratulations, you found a bug in Ceres." - << "Please report it." - << "LAPACK::dgels fatal error." - << "Argument: " << -info << " is invalid."; - } - return static_cast<int>(work); -#endif -} - -LinearSolverTerminationType LAPACK::SolveInPlaceUsingQR( - int num_rows, - int num_cols, - const double* in_lhs, - int work_size, - double* work, - double* rhs_and_solution, - std::string* message) { -#ifdef CERES_NO_LAPACK - LOG(FATAL) << "Ceres was built without a LAPACK library."; - return LINEAR_SOLVER_FATAL_ERROR; -#else - char trans = 'N'; - int m = num_rows; - int n = num_cols; - int nrhs = 1; - int lda = num_rows; - int ldb = num_rows; - int info = 0; - double* lhs = const_cast<double*>(in_lhs); - - dgels_(&trans, - &m, - &n, - &nrhs, - lhs, - &lda, - rhs_and_solution, - &ldb, - work, - &work_size, - &info); - - if (info < 0) { - LOG(FATAL) << "Congratulations, you found a bug in Ceres." - << "Please report it." - << "LAPACK::dgels fatal error." - << "Argument: " << -info << " is invalid."; - } - - *message = "Success."; - return LINEAR_SOLVER_SUCCESS; -#endif -} - -} // namespace internal -} // namespace ceres diff --git a/extern/ceres/internal/ceres/lapack.h b/extern/ceres/internal/ceres/lapack.h deleted file mode 100644 index 5c5bf8bf8b8..00000000000 --- a/extern/ceres/internal/ceres/lapack.h +++ /dev/null @@ -1,101 +0,0 @@ -// Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. -// http://ceres-solver.org/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Author: sameeragarwal@google.com (Sameer Agarwal) - -#ifndef CERES_INTERNAL_LAPACK_H_ -#define CERES_INTERNAL_LAPACK_H_ - -#include <string> - -#include "ceres/internal/port.h" -#include "ceres/linear_solver.h" - -namespace ceres { -namespace internal { - -class LAPACK { - public: - // Solve - // - // lhs * solution = rhs - // - // using a Cholesky factorization. Here - // lhs is a symmetric positive definite matrix. It is assumed to be - // column major and only the lower triangular part of the matrix is - // referenced. - // - // This function uses the LAPACK dpotrf and dpotrs routines. - // - // The return value and the message string together describe whether - // the solver terminated successfully or not and if so, what was the - // reason for failure. - static LinearSolverTerminationType SolveInPlaceUsingCholesky( - int num_rows, - const double* lhs, - double* rhs_and_solution, - std::string* message); - - // The SolveUsingQR function requires a buffer for its temporary - // computation. This function given the size of the lhs matrix will - // return the size of the buffer needed. - static int EstimateWorkSizeForQR(int num_rows, int num_cols); - - // Solve - // - // lhs * solution = rhs - // - // using a dense QR factorization. lhs is an arbitrary (possibly - // rectangular) matrix with full column rank. - // - // work is an array of size work_size that this routine uses for its - // temporary storage. The optimal size of this array can be obtained - // by calling EstimateWorkSizeForQR. - // - // When calling, rhs_and_solution contains the rhs, and upon return - // the first num_col entries are the solution. - // - // This function uses the LAPACK dgels routine. - // - // The return value and the message string together describe whether - // the solver terminated successfully or not and if so, what was the - // reason for failure. - static LinearSolverTerminationType SolveInPlaceUsingQR( - int num_rows, - int num_cols, - const double* lhs, - int work_size, - double* work, - double* rhs_and_solution, - std::string* message); -}; - -} // namespace internal -} // namespace ceres - -#endif // CERES_INTERNAL_LAPACK_H_ diff --git a/extern/ceres/internal/ceres/levenberg_marquardt_strategy.cc b/extern/ceres/internal/ceres/levenberg_marquardt_strategy.cc index cb0e9371b75..2445f5bb99a 100644 --- a/extern/ceres/internal/ceres/levenberg_marquardt_strategy.cc +++ b/extern/ceres/internal/ceres/levenberg_marquardt_strategy.cc @@ -61,7 +61,7 @@ LevenbergMarquardtStrategy::LevenbergMarquardtStrategy( CHECK_GT(max_radius_, 0.0); } -LevenbergMarquardtStrategy::~LevenbergMarquardtStrategy() {} +LevenbergMarquardtStrategy::~LevenbergMarquardtStrategy() = default; TrustRegionStrategy::Summary LevenbergMarquardtStrategy::ComputeStep( const TrustRegionStrategy::PerSolveOptions& per_solve_options, diff --git a/extern/ceres/internal/ceres/levenberg_marquardt_strategy.h b/extern/ceres/internal/ceres/levenberg_marquardt_strategy.h index 12cd463c152..4383a493cde 100644 --- a/extern/ceres/internal/ceres/levenberg_marquardt_strategy.h +++ b/extern/ceres/internal/ceres/levenberg_marquardt_strategy.h @@ -31,8 +31,9 @@ #ifndef CERES_INTERNAL_LEVENBERG_MARQUARDT_STRATEGY_H_ #define CERES_INTERNAL_LEVENBERG_MARQUARDT_STRATEGY_H_ +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/trust_region_strategy.h" namespace ceres { @@ -43,12 +44,12 @@ namespace internal { // K. Madsen, H.B. Nielsen and O. Tingleff. Available to download from // // http://www2.imm.dtu.dk/pubdb/views/edoc_download.php/3215/pdf/imm3215.pdf -class CERES_EXPORT_INTERNAL LevenbergMarquardtStrategy +class CERES_NO_EXPORT LevenbergMarquardtStrategy final : public TrustRegionStrategy { public: explicit LevenbergMarquardtStrategy( const TrustRegionStrategy::Options& options); - virtual ~LevenbergMarquardtStrategy(); + ~LevenbergMarquardtStrategy() override; // TrustRegionStrategy interface TrustRegionStrategy::Summary ComputeStep( @@ -86,4 +87,6 @@ class CERES_EXPORT_INTERNAL LevenbergMarquardtStrategy } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_LEVENBERG_MARQUARDT_STRATEGY_H_ diff --git a/extern/ceres/internal/ceres/line_search.cc b/extern/ceres/internal/ceres/line_search.cc index 7e871a20a53..7e7d97f6d93 100644 --- a/extern/ceres/internal/ceres/line_search.cc +++ b/extern/ceres/internal/ceres/line_search.cc @@ -34,6 +34,7 @@ #include <cmath> #include <iomanip> #include <iostream> // NOLINT +#include <memory> #include "ceres/evaluator.h" #include "ceres/function_sample.h" @@ -65,27 +66,26 @@ ostream& operator<<(ostream& os, const FunctionSample& sample) { return os; } +LineSearch::~LineSearch() = default; + LineSearch::LineSearch(const LineSearch::Options& options) : options_(options) {} -LineSearch* LineSearch::Create(const LineSearchType line_search_type, - const LineSearch::Options& options, - string* error) { - LineSearch* line_search = NULL; +std::unique_ptr<LineSearch> LineSearch::Create( + const LineSearchType line_search_type, + const LineSearch::Options& options, + string* error) { switch (line_search_type) { case ceres::ARMIJO: - line_search = new ArmijoLineSearch(options); - break; + return std::make_unique<ArmijoLineSearch>(options); case ceres::WOLFE: - line_search = new WolfeLineSearch(options); - break; + return std::make_unique<WolfeLineSearch>(options); default: *error = string("Invalid line search algorithm type: ") + LineSearchTypeToString(line_search_type) + string(", unable to create line search."); - return NULL; } - return line_search; + return nullptr; } LineSearchFunction::LineSearchFunction(Evaluator* evaluator) @@ -119,13 +119,13 @@ void LineSearchFunction::Evaluate(const double x, } output->vector_x_is_valid = true; - double* gradient = NULL; + double* gradient = nullptr; if (evaluate_gradient) { output->vector_gradient.resize(direction_.rows(), 1); gradient = output->vector_gradient.data(); } const bool eval_status = evaluator_->Evaluate( - output->vector_x.data(), &(output->value), NULL, gradient, NULL); + output->vector_x.data(), &(output->value), nullptr, gradient, nullptr); if (!eval_status || !std::isfinite(output->value)) { return; @@ -249,12 +249,12 @@ double LineSearch::InterpolatingPolynomialMinimizingStepSize( if (interpolation_type == QUADRATIC) { // Two point interpolation using function values and the // gradient at the lower bound. - samples.push_back(FunctionSample(current.x, current.value)); + samples.emplace_back(current.x, current.value); if (previous.value_is_valid) { // Three point interpolation, using function values and the // gradient at the lower bound. - samples.push_back(FunctionSample(previous.x, previous.value)); + samples.emplace_back(previous.x, previous.value); } } else if (interpolation_type == CUBIC) { // Two point interpolation using the function values and the gradients. diff --git a/extern/ceres/internal/ceres/line_search.h b/extern/ceres/internal/ceres/line_search.h index 634c9717532..c2c744afe00 100644 --- a/extern/ceres/internal/ceres/line_search.h +++ b/extern/ceres/internal/ceres/line_search.h @@ -33,12 +33,13 @@ #ifndef CERES_INTERNAL_LINE_SEARCH_H_ #define CERES_INTERNAL_LINE_SEARCH_H_ +#include <memory> #include <string> #include <vector> #include "ceres/function_sample.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/types.h" namespace ceres { @@ -57,11 +58,11 @@ class LineSearchFunction; // sufficient decrease condition. Depending on the particular // condition used, we get a variety of different line search // algorithms, e.g., Armijo, Wolfe etc. -class LineSearch { +class CERES_NO_EXPORT LineSearch { public: struct Summary; - struct Options { + struct CERES_NO_EXPORT Options { // Degree of the polynomial used to approximate the objective // function. LineSearchInterpolationType interpolation_type = CUBIC; @@ -161,11 +162,12 @@ class LineSearch { }; explicit LineSearch(const LineSearch::Options& options); - virtual ~LineSearch() {} + virtual ~LineSearch(); - static LineSearch* Create(const LineSearchType line_search_type, - const LineSearch::Options& options, - std::string* error); + static std::unique_ptr<LineSearch> Create( + const LineSearchType line_search_type, + const LineSearch::Options& options, + std::string* error); // Perform the line search. // @@ -208,7 +210,7 @@ class LineSearch { // In practice, this object provides access to the objective // function value and the directional derivative of the underlying // optimization problem along a specific search direction. -class LineSearchFunction { +class CERES_NO_EXPORT LineSearchFunction { public: explicit LineSearchFunction(Evaluator* evaluator); void Init(const Vector& position, const Vector& direction); @@ -257,10 +259,9 @@ class LineSearchFunction { // minFunc package by Mark Schmidt. // // For more details: http://www.di.ens.fr/~mschmidt/Software/minFunc.html -class ArmijoLineSearch : public LineSearch { +class CERES_NO_EXPORT ArmijoLineSearch final : public LineSearch { public: explicit ArmijoLineSearch(const LineSearch::Options& options); - virtual ~ArmijoLineSearch() {} private: void DoSearch(double step_size_estimate, @@ -276,10 +277,9 @@ class ArmijoLineSearch : public LineSearch { // // [1] Nocedal J., Wright S., Numerical Optimization, 2nd Ed., Springer, 1999. // [2] http://www.di.ens.fr/~mschmidt/Software/minFunc.html. -class WolfeLineSearch : public LineSearch { +class CERES_NO_EXPORT WolfeLineSearch final : public LineSearch { public: explicit WolfeLineSearch(const LineSearch::Options& options); - virtual ~WolfeLineSearch() {} // Returns true iff either a valid point, or valid bracket are found. bool BracketingPhase(const FunctionSample& initial_position, diff --git a/extern/ceres/internal/ceres/line_search_direction.cc b/extern/ceres/internal/ceres/line_search_direction.cc index 48e6c9812e9..98e335a8029 100644 --- a/extern/ceres/internal/ceres/line_search_direction.cc +++ b/extern/ceres/internal/ceres/line_search_direction.cc @@ -30,7 +30,10 @@ #include "ceres/line_search_direction.h" +#include <memory> + #include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" #include "ceres/line_search_minimizer.h" #include "ceres/low_rank_inverse_hessian.h" #include "glog/logging.h" @@ -38,18 +41,18 @@ namespace ceres { namespace internal { -class SteepestDescent : public LineSearchDirection { +class CERES_NO_EXPORT SteepestDescent final : public LineSearchDirection { public: - virtual ~SteepestDescent() {} bool NextDirection(const LineSearchMinimizer::State& previous, const LineSearchMinimizer::State& current, - Vector* search_direction) { + Vector* search_direction) override { *search_direction = -current.gradient; return true; } }; -class NonlinearConjugateGradient : public LineSearchDirection { +class CERES_NO_EXPORT NonlinearConjugateGradient final + : public LineSearchDirection { public: NonlinearConjugateGradient(const NonlinearConjugateGradientType type, const double function_tolerance) @@ -57,7 +60,7 @@ class NonlinearConjugateGradient : public LineSearchDirection { bool NextDirection(const LineSearchMinimizer::State& previous, const LineSearchMinimizer::State& current, - Vector* search_direction) { + Vector* search_direction) override { double beta = 0.0; Vector gradient_change; switch (type_) { @@ -95,7 +98,7 @@ class NonlinearConjugateGradient : public LineSearchDirection { const double function_tolerance_; }; -class LBFGS : public LineSearchDirection { +class CERES_NO_EXPORT LBFGS final : public LineSearchDirection { public: LBFGS(const int num_parameters, const int max_lbfgs_rank, @@ -105,11 +108,9 @@ class LBFGS : public LineSearchDirection { use_approximate_eigenvalue_bfgs_scaling), is_positive_definite_(true) {} - virtual ~LBFGS() {} - bool NextDirection(const LineSearchMinimizer::State& previous, const LineSearchMinimizer::State& current, - Vector* search_direction) { + Vector* search_direction) override { CHECK(is_positive_definite_) << "Ceres bug: NextDirection() called on L-BFGS after inverse Hessian " << "approximation has become indefinite, please contact the " @@ -141,7 +142,7 @@ class LBFGS : public LineSearchDirection { bool is_positive_definite_; }; -class BFGS : public LineSearchDirection { +class CERES_NO_EXPORT BFGS final : public LineSearchDirection { public: BFGS(const int num_parameters, const bool use_approximate_eigenvalue_scaling) : num_parameters_(num_parameters), @@ -161,11 +162,9 @@ class BFGS : public LineSearchDirection { inverse_hessian_ = Matrix::Identity(num_parameters, num_parameters); } - virtual ~BFGS() {} - bool NextDirection(const LineSearchMinimizer::State& previous, const LineSearchMinimizer::State& current, - Vector* search_direction) { + Vector* search_direction) override { CHECK(is_positive_definite_) << "Ceres bug: NextDirection() called on BFGS after inverse Hessian " << "approximation has become indefinite, please contact the " @@ -338,32 +337,34 @@ class BFGS : public LineSearchDirection { bool is_positive_definite_; }; -LineSearchDirection* LineSearchDirection::Create( +LineSearchDirection::~LineSearchDirection() = default; + +std::unique_ptr<LineSearchDirection> LineSearchDirection::Create( const LineSearchDirection::Options& options) { if (options.type == STEEPEST_DESCENT) { - return new SteepestDescent; + return std::make_unique<SteepestDescent>(); } if (options.type == NONLINEAR_CONJUGATE_GRADIENT) { - return new NonlinearConjugateGradient( + return std::make_unique<NonlinearConjugateGradient>( options.nonlinear_conjugate_gradient_type, options.function_tolerance); } if (options.type == ceres::LBFGS) { - return new ceres::internal::LBFGS( + return std::make_unique<ceres::internal::LBFGS>( options.num_parameters, options.max_lbfgs_rank, options.use_approximate_eigenvalue_bfgs_scaling); } if (options.type == ceres::BFGS) { - return new ceres::internal::BFGS( + return std::make_unique<ceres::internal::BFGS>( options.num_parameters, options.use_approximate_eigenvalue_bfgs_scaling); } LOG(ERROR) << "Unknown line search direction type: " << options.type; - return NULL; + return nullptr; } } // namespace internal diff --git a/extern/ceres/internal/ceres/line_search_direction.h b/extern/ceres/internal/ceres/line_search_direction.h index 2fcf4729ca5..47b256d7133 100644 --- a/extern/ceres/internal/ceres/line_search_direction.h +++ b/extern/ceres/internal/ceres/line_search_direction.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -31,35 +31,31 @@ #ifndef CERES_INTERNAL_LINE_SEARCH_DIRECTION_H_ #define CERES_INTERNAL_LINE_SEARCH_DIRECTION_H_ +#include <memory> + #include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" #include "ceres/line_search_minimizer.h" #include "ceres/types.h" namespace ceres { namespace internal { -class LineSearchDirection { +class CERES_NO_EXPORT LineSearchDirection { public: struct Options { - Options() - : num_parameters(0), - type(LBFGS), - nonlinear_conjugate_gradient_type(FLETCHER_REEVES), - function_tolerance(1e-12), - max_lbfgs_rank(20), - use_approximate_eigenvalue_bfgs_scaling(true) {} - - int num_parameters; - LineSearchDirectionType type; - NonlinearConjugateGradientType nonlinear_conjugate_gradient_type; - double function_tolerance; - int max_lbfgs_rank; - bool use_approximate_eigenvalue_bfgs_scaling; + int num_parameters{0}; + LineSearchDirectionType type{LBFGS}; + NonlinearConjugateGradientType nonlinear_conjugate_gradient_type{ + FLETCHER_REEVES}; + double function_tolerance{1e-12}; + int max_lbfgs_rank{20}; + bool use_approximate_eigenvalue_bfgs_scaling{true}; }; - static LineSearchDirection* Create(const Options& options); + static std::unique_ptr<LineSearchDirection> Create(const Options& options); - virtual ~LineSearchDirection() {} + virtual ~LineSearchDirection(); virtual bool NextDirection(const LineSearchMinimizer::State& previous, const LineSearchMinimizer::State& current, Vector* search_direction) = 0; diff --git a/extern/ceres/internal/ceres/line_search_minimizer.cc b/extern/ceres/internal/ceres/line_search_minimizer.cc index ea1c5072a14..ad1e1852386 100644 --- a/extern/ceres/internal/ceres/line_search_minimizer.cc +++ b/extern/ceres/internal/ceres/line_search_minimizer.cc @@ -51,7 +51,7 @@ #include "ceres/array_utils.h" #include "ceres/evaluator.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/line_search.h" #include "ceres/line_search_direction.h" #include "ceres/stringprintf.h" @@ -171,8 +171,8 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options, line_search_direction_options.max_lbfgs_rank = options.max_lbfgs_rank; line_search_direction_options.use_approximate_eigenvalue_bfgs_scaling = options.use_approximate_eigenvalue_bfgs_scaling; - std::unique_ptr<LineSearchDirection> line_search_direction( - LineSearchDirection::Create(line_search_direction_options)); + std::unique_ptr<LineSearchDirection> line_search_direction = + LineSearchDirection::Create(line_search_direction_options); LineSearchFunction line_search_function(evaluator); @@ -280,8 +280,8 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options, << options.max_num_line_search_direction_restarts << " [max]."; } - line_search_direction.reset( - LineSearchDirection::Create(line_search_direction_options)); + line_search_direction = + LineSearchDirection::Create(line_search_direction_options); current_state.search_direction = -current_state.gradient; } diff --git a/extern/ceres/internal/ceres/line_search_minimizer.h b/extern/ceres/internal/ceres/line_search_minimizer.h index 79e8dc9e49a..9a0e994dcfc 100644 --- a/extern/ceres/internal/ceres/line_search_minimizer.h +++ b/extern/ceres/internal/ceres/line_search_minimizer.h @@ -32,6 +32,7 @@ #define CERES_INTERNAL_LINE_SEARCH_MINIMIZER_H_ #include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" #include "ceres/minimizer.h" #include "ceres/solver.h" #include "ceres/types.h" @@ -43,7 +44,7 @@ namespace internal { // Generic line search minimization algorithm. // // For example usage, see SolverImpl::Minimize. -class LineSearchMinimizer : public Minimizer { +class CERES_NO_EXPORT LineSearchMinimizer final : public Minimizer { public: struct State { State(int num_parameters, int num_effective_parameters) @@ -63,7 +64,6 @@ class LineSearchMinimizer : public Minimizer { double step_size; }; - ~LineSearchMinimizer() {} void Minimize(const Minimizer::Options& options, double* parameters, Solver::Summary* summary) final; diff --git a/extern/ceres/internal/ceres/line_search_preprocessor.cc b/extern/ceres/internal/ceres/line_search_preprocessor.cc index 6a69425e764..26b8d99a4c7 100644 --- a/extern/ceres/internal/ceres/line_search_preprocessor.cc +++ b/extern/ceres/internal/ceres/line_search_preprocessor.cc @@ -63,15 +63,13 @@ bool SetupEvaluator(PreprocessedProblem* pp) { pp->evaluator_options.context = pp->problem->context(); pp->evaluator_options.evaluation_callback = pp->reduced_program->mutable_evaluation_callback(); - pp->evaluator.reset(Evaluator::Create( - pp->evaluator_options, pp->reduced_program.get(), &pp->error)); - return (pp->evaluator.get() != NULL); + pp->evaluator = Evaluator::Create( + pp->evaluator_options, pp->reduced_program.get(), &pp->error); + return (pp->evaluator.get() != nullptr); } } // namespace -LineSearchPreprocessor::~LineSearchPreprocessor() {} - bool LineSearchPreprocessor::Preprocess(const Solver::Options& options, ProblemImpl* problem, PreprocessedProblem* pp) { @@ -85,10 +83,10 @@ bool LineSearchPreprocessor::Preprocess(const Solver::Options& options, return false; } - pp->reduced_program.reset(program->CreateReducedProgram( - &pp->removed_parameter_blocks, &pp->fixed_cost, &pp->error)); + pp->reduced_program = program->CreateReducedProgram( + &pp->removed_parameter_blocks, &pp->fixed_cost, &pp->error); - if (pp->reduced_program.get() == NULL) { + if (pp->reduced_program.get() == nullptr) { return false; } diff --git a/extern/ceres/internal/ceres/line_search_preprocessor.h b/extern/ceres/internal/ceres/line_search_preprocessor.h index bd426c7f2f6..27e9c2db9b9 100644 --- a/extern/ceres/internal/ceres/line_search_preprocessor.h +++ b/extern/ceres/internal/ceres/line_search_preprocessor.h @@ -31,15 +31,15 @@ #ifndef CERES_INTERNAL_LINE_SEARCH_PREPROCESSOR_H_ #define CERES_INTERNAL_LINE_SEARCH_PREPROCESSOR_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/preprocessor.h" namespace ceres { namespace internal { -class CERES_EXPORT_INTERNAL LineSearchPreprocessor : public Preprocessor { +class CERES_NO_EXPORT LineSearchPreprocessor final : public Preprocessor { public: - virtual ~LineSearchPreprocessor(); bool Preprocess(const Solver::Options& options, ProblemImpl* problem, PreprocessedProblem* preprocessed_problem) final; @@ -48,4 +48,6 @@ class CERES_EXPORT_INTERNAL LineSearchPreprocessor : public Preprocessor { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_LINE_SEARCH_PREPROCESSOR_H_ diff --git a/extern/ceres/internal/ceres/linear_least_squares_problems.cc b/extern/ceres/internal/ceres/linear_least_squares_problems.cc index 299051c5bcf..2d415af338f 100644 --- a/extern/ceres/internal/ceres/linear_least_squares_problems.cc +++ b/extern/ceres/internal/ceres/linear_least_squares_problems.cc @@ -49,7 +49,8 @@ namespace internal { using std::string; -LinearLeastSquaresProblem* CreateLinearLeastSquaresProblemFromId(int id) { +std::unique_ptr<LinearLeastSquaresProblem> +CreateLinearLeastSquaresProblemFromId(int id) { switch (id) { case 0: return LinearLeastSquaresProblem0(); @@ -64,7 +65,7 @@ LinearLeastSquaresProblem* CreateLinearLeastSquaresProblemFromId(int id) { default: LOG(FATAL) << "Unknown problem id requested " << id; } - return NULL; + return nullptr; } /* @@ -85,15 +86,16 @@ D = [1 x_D = [1.78448275; 2.82327586;] */ -LinearLeastSquaresProblem* LinearLeastSquaresProblem0() { - LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem; +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem0() { + std::unique_ptr<LinearLeastSquaresProblem> problem = + std::make_unique<LinearLeastSquaresProblem>(); - TripletSparseMatrix* A = new TripletSparseMatrix(3, 2, 6); - problem->b.reset(new double[3]); - problem->D.reset(new double[2]); + auto A = std::make_unique<TripletSparseMatrix>(3, 2, 6); + problem->b = std::make_unique<double[]>(3); + problem->D = std::make_unique<double[]>(2); - problem->x.reset(new double[2]); - problem->x_D.reset(new double[2]); + problem->x = std::make_unique<double[]>(2); + problem->x_D = std::make_unique<double[]>(2); int* Ai = A->mutable_rows(); int* Aj = A->mutable_cols(); @@ -115,7 +117,7 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem0() { Ax[4] = 6; Ax[5] = -10; A->set_num_nonzeros(6); - problem->A.reset(A); + problem->A = std::move(A); problem->b[0] = 8; problem->b[1] = 18; @@ -161,7 +163,7 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem0() { S = [ 42.3419 -1.4000 -11.5806 -1.4000 2.6000 1.0000 - 11.5806 1.0000 31.1935] + -11.5806 1.0000 31.1935] r = [ 4.3032 5.4000 @@ -181,15 +183,16 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem0() { // BlockSparseMatrix version of this problem. // TripletSparseMatrix version. -LinearLeastSquaresProblem* LinearLeastSquaresProblem1() { +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem1() { int num_rows = 6; int num_cols = 5; - LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem; - TripletSparseMatrix* A = - new TripletSparseMatrix(num_rows, num_cols, num_rows * num_cols); - problem->b.reset(new double[num_rows]); - problem->D.reset(new double[num_cols]); + std::unique_ptr<LinearLeastSquaresProblem> problem = + std::make_unique<LinearLeastSquaresProblem>(); + auto A = std::make_unique<TripletSparseMatrix>( + num_rows, num_cols, num_rows * num_cols); + problem->b = std::make_unique<double[]>(num_rows); + problem->D = std::make_unique<double[]>(num_cols); problem->num_eliminate_blocks = 2; int* rows = A->mutable_rows(); @@ -271,7 +274,7 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem1() { A->set_num_nonzeros(nnz); CHECK(A->IsValid()); - problem->A.reset(A); + problem->A = std::move(A); for (int i = 0; i < num_cols; ++i) { problem->D.get()[i] = 1; @@ -285,21 +288,23 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem1() { } // BlockSparseMatrix version -LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem2() { int num_rows = 6; int num_cols = 5; - LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem; + std::unique_ptr<LinearLeastSquaresProblem> problem = + std::make_unique<LinearLeastSquaresProblem>(); - problem->b.reset(new double[num_rows]); - problem->D.reset(new double[num_cols]); + problem->b = std::make_unique<double[]>(num_rows); + problem->D = std::make_unique<double[]>(num_cols); problem->num_eliminate_blocks = 2; - CompressedRowBlockStructure* bs = new CompressedRowBlockStructure; - std::unique_ptr<double[]> values(new double[num_rows * num_cols]); + auto* bs = new CompressedRowBlockStructure; + std::unique_ptr<double[]> values = + std::make_unique<double[]>(num_rows * num_cols); for (int c = 0; c < num_cols; ++c) { - bs->cols.push_back(Block()); + bs->cols.emplace_back(); bs->cols.back().size = 1; bs->cols.back().position = c; } @@ -311,12 +316,12 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { values[nnz++] = 1; values[nnz++] = 2; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 0; - row.cells.push_back(Cell(0, 0)); - row.cells.push_back(Cell(2, 1)); + row.cells.emplace_back(0, 0); + row.cells.emplace_back(2, 1); } // Row 2 @@ -324,12 +329,12 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { values[nnz++] = 3; values[nnz++] = 4; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 1; - row.cells.push_back(Cell(0, 2)); - row.cells.push_back(Cell(3, 3)); + row.cells.emplace_back(0, 2); + row.cells.emplace_back(3, 3); } // Row 3 @@ -337,12 +342,12 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { values[nnz++] = 5; values[nnz++] = 6; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 2; - row.cells.push_back(Cell(1, 4)); - row.cells.push_back(Cell(4, 5)); + row.cells.emplace_back(1, 4); + row.cells.emplace_back(4, 5); } // Row 4 @@ -350,12 +355,12 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { values[nnz++] = 7; values[nnz++] = 8; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 3; - row.cells.push_back(Cell(1, 6)); - row.cells.push_back(Cell(2, 7)); + row.cells.emplace_back(1, 6); + row.cells.emplace_back(2, 7); } // Row 5 @@ -363,12 +368,12 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { values[nnz++] = 9; values[nnz++] = 1; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 4; - row.cells.push_back(Cell(1, 8)); - row.cells.push_back(Cell(2, 9)); + row.cells.emplace_back(1, 8); + row.cells.emplace_back(2, 9); } // Row 6 @@ -377,16 +382,16 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { values[nnz++] = 1; values[nnz++] = 1; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 5; - row.cells.push_back(Cell(2, 10)); - row.cells.push_back(Cell(3, 11)); - row.cells.push_back(Cell(4, 12)); + row.cells.emplace_back(2, 10); + row.cells.emplace_back(3, 11); + row.cells.emplace_back(4, 12); } - BlockSparseMatrix* A = new BlockSparseMatrix(bs); + auto A = std::make_unique<BlockSparseMatrix>(bs); memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values())); for (int i = 0; i < num_cols; ++i) { @@ -397,7 +402,7 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { problem->b.get()[i] = i; } - problem->A.reset(A); + problem->A = std::move(A); return problem; } @@ -418,21 +423,23 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { 5] */ // BlockSparseMatrix version -LinearLeastSquaresProblem* LinearLeastSquaresProblem3() { +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem3() { int num_rows = 5; int num_cols = 2; - LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem; + std::unique_ptr<LinearLeastSquaresProblem> problem = + std::make_unique<LinearLeastSquaresProblem>(); - problem->b.reset(new double[num_rows]); - problem->D.reset(new double[num_cols]); + problem->b = std::make_unique<double[]>(num_rows); + problem->D = std::make_unique<double[]>(num_cols); problem->num_eliminate_blocks = 2; - CompressedRowBlockStructure* bs = new CompressedRowBlockStructure; - std::unique_ptr<double[]> values(new double[num_rows * num_cols]); + auto* bs = new CompressedRowBlockStructure; + std::unique_ptr<double[]> values = + std::make_unique<double[]>(num_rows * num_cols); for (int c = 0; c < num_cols; ++c) { - bs->cols.push_back(Block()); + bs->cols.emplace_back(); bs->cols.back().size = 1; bs->cols.back().position = c; } @@ -442,54 +449,54 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem3() { // Row 1 { values[nnz++] = 1; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 0; - row.cells.push_back(Cell(0, 0)); + row.cells.emplace_back(0, 0); } // Row 2 { values[nnz++] = 3; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 1; - row.cells.push_back(Cell(0, 1)); + row.cells.emplace_back(0, 1); } // Row 3 { values[nnz++] = 5; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 2; - row.cells.push_back(Cell(1, 2)); + row.cells.emplace_back(1, 2); } // Row 4 { values[nnz++] = 7; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 3; - row.cells.push_back(Cell(1, 3)); + row.cells.emplace_back(1, 3); } // Row 5 { values[nnz++] = 9; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 4; - row.cells.push_back(Cell(1, 4)); + row.cells.emplace_back(1, 4); } - BlockSparseMatrix* A = new BlockSparseMatrix(bs); + auto A = std::make_unique<BlockSparseMatrix>(bs); memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values())); for (int i = 0; i < num_cols; ++i) { @@ -500,7 +507,7 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem3() { problem->b.get()[i] = i; } - problem->A.reset(A); + problem->A = std::move(A); return problem; } @@ -525,29 +532,31 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem3() { // // NOTE: This problem is too small and rank deficient to be solved without // the diagonal regularization. -LinearLeastSquaresProblem* LinearLeastSquaresProblem4() { +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem4() { int num_rows = 3; int num_cols = 7; - LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem; + std::unique_ptr<LinearLeastSquaresProblem> problem = + std::make_unique<LinearLeastSquaresProblem>(); - problem->b.reset(new double[num_rows]); - problem->D.reset(new double[num_cols]); + problem->b = std::make_unique<double[]>(num_rows); + problem->D = std::make_unique<double[]>(num_cols); problem->num_eliminate_blocks = 1; - CompressedRowBlockStructure* bs = new CompressedRowBlockStructure; - std::unique_ptr<double[]> values(new double[num_rows * num_cols]); + auto* bs = new CompressedRowBlockStructure; + std::unique_ptr<double[]> values = + std::make_unique<double[]>(num_rows * num_cols); // Column block structure - bs->cols.push_back(Block()); + bs->cols.emplace_back(); bs->cols.back().size = 2; bs->cols.back().position = 0; - bs->cols.push_back(Block()); + bs->cols.emplace_back(); bs->cols.back().size = 3; bs->cols.back().position = 2; - bs->cols.push_back(Block()); + bs->cols.emplace_back(); bs->cols.back().size = 2; bs->cols.back().position = 5; @@ -555,18 +564,18 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem4() { // Row 1 & 2 { - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 2; row.block.position = 0; - row.cells.push_back(Cell(0, nnz)); + row.cells.emplace_back(0, nnz); values[nnz++] = 1; values[nnz++] = 2; values[nnz++] = 1; values[nnz++] = 4; - row.cells.push_back(Cell(2, nnz)); + row.cells.emplace_back(2, nnz); values[nnz++] = 1; values[nnz++] = 1; values[nnz++] = 5; @@ -575,22 +584,22 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem4() { // Row 3 { - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 2; - row.cells.push_back(Cell(1, nnz)); + row.cells.emplace_back(1, nnz); values[nnz++] = 9; values[nnz++] = 0; values[nnz++] = 0; - row.cells.push_back(Cell(2, nnz)); + row.cells.emplace_back(2, nnz); values[nnz++] = 3; values[nnz++] = 1; } - BlockSparseMatrix* A = new BlockSparseMatrix(bs); + auto A = std::make_unique<BlockSparseMatrix>(bs); memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values())); for (int i = 0; i < num_cols; ++i) { @@ -601,7 +610,7 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem4() { problem->b.get()[i] = i; } - problem->A.reset(A); + problem->A = std::move(A); return problem; } @@ -616,15 +625,15 @@ bool DumpLinearLeastSquaresProblemToConsole(const SparseMatrix* A, A->ToDenseMatrix(&AA); LOG(INFO) << "A^T: \n" << AA.transpose(); - if (D != NULL) { + if (D != nullptr) { LOG(INFO) << "A's appended diagonal:\n" << ConstVectorRef(D, A->num_cols()); } - if (b != NULL) { + if (b != nullptr) { LOG(INFO) << "b: \n" << ConstVectorRef(b, A->num_rows()); } - if (x != NULL) { + if (x != nullptr) { LOG(INFO) << "x: \n" << ConstVectorRef(x, A->num_cols()); } return true; @@ -673,21 +682,21 @@ bool DumpLinearLeastSquaresProblemToTextFile(const string& filename_base, A->num_cols()); } - if (D != NULL) { + if (D != nullptr) { string filename = filename_base + "_D.txt"; WriteArrayToFileOrDie(filename, D, A->num_cols()); StringAppendF( &matlab_script, "lsqp.D = load('%s', '-ascii');\n", filename.c_str()); } - if (b != NULL) { + if (b != nullptr) { string filename = filename_base + "_b.txt"; WriteArrayToFileOrDie(filename, b, A->num_rows()); StringAppendF( &matlab_script, "lsqp.b = load('%s', '-ascii');\n", filename.c_str()); } - if (x != NULL) { + if (x != nullptr) { string filename = filename_base + "_x.txt"; WriteArrayToFileOrDie(filename, x, A->num_cols()); StringAppendF( diff --git a/extern/ceres/internal/ceres/linear_least_squares_problems.h b/extern/ceres/internal/ceres/linear_least_squares_problems.h index cddaa9fd4d7..a1f67eb306e 100644 --- a/extern/ceres/internal/ceres/linear_least_squares_problems.h +++ b/extern/ceres/internal/ceres/linear_least_squares_problems.h @@ -35,7 +35,8 @@ #include <string> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/sparse_matrix.h" namespace ceres { @@ -43,15 +44,15 @@ namespace internal { // Structure defining a linear least squares problem and if possible // ground truth solutions. To be used by various LinearSolver tests. -struct CERES_EXPORT_INTERNAL LinearLeastSquaresProblem { - LinearLeastSquaresProblem() : num_eliminate_blocks(0) {} +struct CERES_NO_EXPORT LinearLeastSquaresProblem { + LinearLeastSquaresProblem() = default; std::unique_ptr<SparseMatrix> A; std::unique_ptr<double[]> b; std::unique_ptr<double[]> D; // If using the schur eliminator then how many of the variable // blocks are e_type blocks. - int num_eliminate_blocks; + int num_eliminate_blocks{0}; // Solution to min_x |Ax - b|^2 std::unique_ptr<double[]> x; @@ -60,17 +61,23 @@ struct CERES_EXPORT_INTERNAL LinearLeastSquaresProblem { }; // Factories for linear least squares problem. -CERES_EXPORT_INTERNAL LinearLeastSquaresProblem* +CERES_NO_EXPORT std::unique_ptr<LinearLeastSquaresProblem> CreateLinearLeastSquaresProblemFromId(int id); -LinearLeastSquaresProblem* LinearLeastSquaresProblem0(); -LinearLeastSquaresProblem* LinearLeastSquaresProblem1(); -LinearLeastSquaresProblem* LinearLeastSquaresProblem2(); -LinearLeastSquaresProblem* LinearLeastSquaresProblem3(); -LinearLeastSquaresProblem* LinearLeastSquaresProblem4(); +CERES_NO_EXPORT +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem0(); +CERES_NO_EXPORT +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem1(); +CERES_NO_EXPORT +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem2(); +CERES_NO_EXPORT +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem3(); +CERES_NO_EXPORT +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem4(); // Write the linear least squares problem to disk. The exact format // depends on dump_format_type. +CERES_NO_EXPORT bool DumpLinearLeastSquaresProblem(const std::string& filename_base, DumpFormatType dump_format_type, const SparseMatrix* A, @@ -81,4 +88,6 @@ bool DumpLinearLeastSquaresProblem(const std::string& filename_base, } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_LINEAR_LEAST_SQUARES_PROBLEMS_H_ diff --git a/extern/ceres/internal/ceres/linear_operator.cc b/extern/ceres/internal/ceres/linear_operator.cc index 548c724267e..88b7cc752d4 100644 --- a/extern/ceres/internal/ceres/linear_operator.cc +++ b/extern/ceres/internal/ceres/linear_operator.cc @@ -33,7 +33,7 @@ namespace ceres { namespace internal { -LinearOperator::~LinearOperator() {} +LinearOperator::~LinearOperator() = default; } // namespace internal } // namespace ceres diff --git a/extern/ceres/internal/ceres/linear_operator.h b/extern/ceres/internal/ceres/linear_operator.h index 9c59fc39c13..c9e6188e2e8 100644 --- a/extern/ceres/internal/ceres/linear_operator.h +++ b/extern/ceres/internal/ceres/linear_operator.h @@ -33,7 +33,7 @@ #ifndef CERES_INTERNAL_LINEAR_OPERATOR_H_ #define CERES_INTERNAL_LINEAR_OPERATOR_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/types.h" namespace ceres { @@ -41,7 +41,7 @@ namespace internal { // This is an abstract base class for linear operators. It supports // access to size information and left and right multiply operators. -class CERES_EXPORT_INTERNAL LinearOperator { +class CERES_NO_EXPORT LinearOperator { public: virtual ~LinearOperator(); diff --git a/extern/ceres/internal/ceres/linear_solver.cc b/extern/ceres/internal/ceres/linear_solver.cc index 6cae2488f07..fe324f81301 100644 --- a/extern/ceres/internal/ceres/linear_solver.cc +++ b/extern/ceres/internal/ceres/linear_solver.cc @@ -30,10 +30,13 @@ #include "ceres/linear_solver.h" +#include <memory> + #include "ceres/cgnr_solver.h" #include "ceres/dense_normal_cholesky_solver.h" #include "ceres/dense_qr_solver.h" #include "ceres/dynamic_sparse_normal_cholesky_solver.h" +#include "ceres/internal/config.h" #include "ceres/iterative_schur_complement_solver.h" #include "ceres/schur_complement_solver.h" #include "ceres/sparse_normal_cholesky_solver.h" @@ -43,7 +46,7 @@ namespace ceres { namespace internal { -LinearSolver::~LinearSolver() {} +LinearSolver::~LinearSolver() = default; LinearSolverType LinearSolver::LinearSolverForZeroEBlocks( LinearSolverType linear_solver_type) { @@ -69,50 +72,51 @@ LinearSolverType LinearSolver::LinearSolverForZeroEBlocks( return linear_solver_type; } -LinearSolver* LinearSolver::Create(const LinearSolver::Options& options) { - CHECK(options.context != NULL); +std::unique_ptr<LinearSolver> LinearSolver::Create( + const LinearSolver::Options& options) { + CHECK(options.context != nullptr); switch (options.type) { case CGNR: - return new CgnrSolver(options); + return std::make_unique<CgnrSolver>(options); case SPARSE_NORMAL_CHOLESKY: #if defined(CERES_NO_SPARSE) - return NULL; + return nullptr; #else if (options.dynamic_sparsity) { - return new DynamicSparseNormalCholeskySolver(options); + return std::make_unique<DynamicSparseNormalCholeskySolver>(options); } - return new SparseNormalCholeskySolver(options); + return std::make_unique<SparseNormalCholeskySolver>(options); #endif case SPARSE_SCHUR: #if defined(CERES_NO_SPARSE) - return NULL; + return nullptr; #else - return new SparseSchurComplementSolver(options); + return std::make_unique<SparseSchurComplementSolver>(options); #endif case DENSE_SCHUR: - return new DenseSchurComplementSolver(options); + return std::make_unique<DenseSchurComplementSolver>(options); case ITERATIVE_SCHUR: if (options.use_explicit_schur_complement) { - return new SparseSchurComplementSolver(options); + return std::make_unique<SparseSchurComplementSolver>(options); } else { - return new IterativeSchurComplementSolver(options); + return std::make_unique<IterativeSchurComplementSolver>(options); } case DENSE_QR: - return new DenseQRSolver(options); + return std::make_unique<DenseQRSolver>(options); case DENSE_NORMAL_CHOLESKY: - return new DenseNormalCholeskySolver(options); + return std::make_unique<DenseNormalCholeskySolver>(options); default: LOG(FATAL) << "Unknown linear solver type :" << options.type; - return NULL; // MSVC doesn't understand that LOG(FATAL) never returns. + return nullptr; // MSVC doesn't understand that LOG(FATAL) never returns. } } diff --git a/extern/ceres/internal/ceres/linear_solver.h b/extern/ceres/internal/ceres/linear_solver.h index 49c6527acc9..2f709c297e5 100644 --- a/extern/ceres/internal/ceres/linear_solver.h +++ b/extern/ceres/internal/ceres/linear_solver.h @@ -36,6 +36,7 @@ #include <cstddef> #include <map> +#include <memory> #include <string> #include <vector> @@ -45,7 +46,8 @@ #include "ceres/context_impl.h" #include "ceres/dense_sparse_matrix.h" #include "ceres/execution_summary.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/triplet_sparse_matrix.h" #include "ceres/types.h" #include "glog/logging.h" @@ -101,7 +103,7 @@ class LinearOperator; // The Options struct configures the LinearSolver object for its // lifetime. The PerSolveOptions struct is used to specify options for // a particular Solve call. -class CERES_EXPORT_INTERNAL LinearSolver { +class CERES_NO_EXPORT LinearSolver { public: struct Options { LinearSolverType type = SPARSE_NORMAL_CHOLESKY; @@ -284,11 +286,11 @@ class CERES_EXPORT_INTERNAL LinearSolver { // issues. Further, this calls are not expected to be frequent or // performance sensitive. virtual std::map<std::string, CallStatistics> Statistics() const { - return std::map<std::string, CallStatistics>(); + return {}; } // Factory - static LinearSolver* Create(const Options& options); + static std::unique_ptr<LinearSolver> Create(const Options& options); }; // This templated subclass of LinearSolver serves as a base class for @@ -301,12 +303,11 @@ class CERES_EXPORT_INTERNAL LinearSolver { template <typename MatrixType> class TypedLinearSolver : public LinearSolver { public: - virtual ~TypedLinearSolver() {} - virtual LinearSolver::Summary Solve( + LinearSolver::Summary Solve( LinearOperator* A, const double* b, const LinearSolver::PerSolveOptions& per_solve_options, - double* x) { + double* x) override { ScopedExecutionTimer total_time("LinearSolver::Solve", &execution_summary_); CHECK(A != nullptr); CHECK(b != nullptr); @@ -314,7 +315,7 @@ class TypedLinearSolver : public LinearSolver { return SolveImpl(down_cast<MatrixType*>(A), b, per_solve_options, x); } - virtual std::map<std::string, CallStatistics> Statistics() const { + std::map<std::string, CallStatistics> Statistics() const override { return execution_summary_.statistics(); } @@ -340,4 +341,6 @@ typedef TypedLinearSolver<TripletSparseMatrix> TripletSparseMatrixSolver; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_LINEAR_SOLVER_H_ diff --git a/extern/ceres/internal/ceres/local_parameterization.cc b/extern/ceres/internal/ceres/local_parameterization.cc index 62947f06fcc..db6f95a1984 100644 --- a/extern/ceres/internal/ceres/local_parameterization.cc +++ b/extern/ceres/internal/ceres/local_parameterization.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -43,7 +43,7 @@ namespace ceres { using std::vector; -LocalParameterization::~LocalParameterization() {} +LocalParameterization::~LocalParameterization() = default; bool LocalParameterization::MultiplyByJacobian(const double* x, const int num_rows, @@ -107,8 +107,8 @@ SubsetParameterization::SubsetParameterization( << "of the parameter block."; CHECK(std::adjacent_find(constant.begin(), constant.end()) == constant.end()) << "The set of constant parameters cannot contain duplicates"; - for (int i = 0; i < constant_parameters.size(); ++i) { - constancy_mask_[constant_parameters[i]] = 1; + for (int parameter : constant_parameters) { + constancy_mask_[parameter] = 1; } } diff --git a/extern/ceres/internal/ceres/loss_function.cc b/extern/ceres/internal/ceres/loss_function.cc index 353f29a5bf5..3392b3b7f81 100644 --- a/extern/ceres/internal/ceres/loss_function.cc +++ b/extern/ceres/internal/ceres/loss_function.cc @@ -39,6 +39,8 @@ namespace ceres { +LossFunction::~LossFunction() = default; + void TrivialLoss::Evaluate(double s, double rho[3]) const { rho[0] = s; rho[1] = 1.0; @@ -161,7 +163,7 @@ void ComposedLoss::Evaluate(double s, double rho[3]) const { } void ScaledLoss::Evaluate(double s, double rho[3]) const { - if (rho_.get() == NULL) { + if (rho_.get() == nullptr) { rho[0] = a_ * s; rho[1] = a_; rho[2] = 0.0; diff --git a/extern/ceres/internal/ceres/low_rank_inverse_hessian.cc b/extern/ceres/internal/ceres/low_rank_inverse_hessian.cc index c73e5dbf88d..2fd1ac83f00 100644 --- a/extern/ceres/internal/ceres/low_rank_inverse_hessian.cc +++ b/extern/ceres/internal/ceres/low_rank_inverse_hessian.cc @@ -127,9 +127,7 @@ void LowRankInverseHessian::RightMultiply(const double* x_ptr, const int num_corrections = indices_.size(); Vector alpha(num_corrections); - for (list<int>::const_reverse_iterator it = indices_.rbegin(); - it != indices_.rend(); - ++it) { + for (auto it = indices_.rbegin(); it != indices_.rend(); ++it) { const double alpha_i = delta_x_history_.col(*it).dot(search_direction) / delta_x_dot_delta_gradient_(*it); search_direction -= alpha_i * delta_gradient_history_.col(*it); diff --git a/extern/ceres/internal/ceres/low_rank_inverse_hessian.h b/extern/ceres/internal/ceres/low_rank_inverse_hessian.h index 0028a988923..36519360262 100644 --- a/extern/ceres/internal/ceres/low_rank_inverse_hessian.h +++ b/extern/ceres/internal/ceres/low_rank_inverse_hessian.h @@ -37,6 +37,7 @@ #include <list> #include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" #include "ceres/linear_operator.h" namespace ceres { @@ -59,7 +60,7 @@ namespace internal { // Byrd, R. H.; Nocedal, J.; Schnabel, R. B. (1994). // "Representations of Quasi-Newton Matrices and their use in // Limited Memory Methods". Mathematical Programming 63 (4): -class LowRankInverseHessian : public LinearOperator { +class CERES_NO_EXPORT LowRankInverseHessian final : public LinearOperator { public: // num_parameters is the row/column size of the Hessian. // max_num_corrections is the rank of the Hessian approximation. @@ -73,7 +74,6 @@ class LowRankInverseHessian : public LinearOperator { LowRankInverseHessian(int num_parameters, int max_num_corrections, bool use_approximate_eigenvalue_scaling); - virtual ~LowRankInverseHessian() {} // Update the low rank approximation. delta_x is the change in the // domain of Hessian, and delta_gradient is the change in the diff --git a/extern/ceres/internal/ceres/manifold.cc b/extern/ceres/internal/ceres/manifold.cc new file mode 100644 index 00000000000..f412a793f93 --- /dev/null +++ b/extern/ceres/internal/ceres/manifold.cc @@ -0,0 +1,321 @@ +#include "ceres/manifold.h" + +#include <algorithm> +#include <cmath> + +#include "ceres/internal/eigen.h" +#include "ceres/internal/fixed_array.h" +#include "glog/logging.h" + +namespace ceres { +namespace { + +struct CeresQuaternionOrder { + static constexpr int kW = 0; + static constexpr int kX = 1; + static constexpr int kY = 2; + static constexpr int kZ = 3; +}; + +struct EigenQuaternionOrder { + static constexpr int kW = 3; + static constexpr int kX = 0; + static constexpr int kY = 1; + static constexpr int kZ = 2; +}; + +template <typename Order> +inline void QuaternionPlusImpl(const double* x, + const double* delta, + double* x_plus_delta) { + // x_plus_delta = QuaternionProduct(q_delta, x), where q_delta is the + // quaternion constructed from delta. + const double norm_delta = std::sqrt( + delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]); + + if (norm_delta == 0.0) { + for (int i = 0; i < 4; ++i) { + x_plus_delta[i] = x[i]; + } + return; + } + + const double sin_delta_by_delta = (std::sin(norm_delta) / norm_delta); + double q_delta[4]; + q_delta[Order::kW] = std::cos(norm_delta); + q_delta[Order::kX] = sin_delta_by_delta * delta[0]; + q_delta[Order::kY] = sin_delta_by_delta * delta[1]; + q_delta[Order::kZ] = sin_delta_by_delta * delta[2]; + + x_plus_delta[Order::kW] = + q_delta[Order::kW] * x[Order::kW] - q_delta[Order::kX] * x[Order::kX] - + q_delta[Order::kY] * x[Order::kY] - q_delta[Order::kZ] * x[Order::kZ]; + x_plus_delta[Order::kX] = + q_delta[Order::kW] * x[Order::kX] + q_delta[Order::kX] * x[Order::kW] + + q_delta[Order::kY] * x[Order::kZ] - q_delta[Order::kZ] * x[Order::kY]; + x_plus_delta[Order::kY] = + q_delta[Order::kW] * x[Order::kY] - q_delta[Order::kX] * x[Order::kZ] + + q_delta[Order::kY] * x[Order::kW] + q_delta[Order::kZ] * x[Order::kX]; + x_plus_delta[Order::kZ] = + q_delta[Order::kW] * x[Order::kZ] + q_delta[Order::kX] * x[Order::kY] - + q_delta[Order::kY] * x[Order::kX] + q_delta[Order::kZ] * x[Order::kW]; +} + +template <typename Order> +inline void QuaternionPlusJacobianImpl(const double* x, double* jacobian_ptr) { + Eigen::Map<Eigen::Matrix<double, 4, 3, Eigen::RowMajor>> jacobian( + jacobian_ptr); + + jacobian(Order::kW, 0) = -x[Order::kX]; + jacobian(Order::kW, 1) = -x[Order::kY]; + jacobian(Order::kW, 2) = -x[Order::kZ]; + jacobian(Order::kX, 0) = x[Order::kW]; + jacobian(Order::kX, 1) = x[Order::kZ]; + jacobian(Order::kX, 2) = -x[Order::kY]; + jacobian(Order::kY, 0) = -x[Order::kZ]; + jacobian(Order::kY, 1) = x[Order::kW]; + jacobian(Order::kY, 2) = x[Order::kX]; + jacobian(Order::kZ, 0) = x[Order::kY]; + jacobian(Order::kZ, 1) = -x[Order::kX]; + jacobian(Order::kZ, 2) = x[Order::kW]; +} + +template <typename Order> +inline void QuaternionMinusImpl(const double* y, + const double* x, + double* y_minus_x) { + // ambient_y_minus_x = QuaternionProduct(y, -x) where -x is the conjugate of + // x. + double ambient_y_minus_x[4]; + ambient_y_minus_x[Order::kW] = + y[Order::kW] * x[Order::kW] + y[Order::kX] * x[Order::kX] + + y[Order::kY] * x[Order::kY] + y[Order::kZ] * x[Order::kZ]; + ambient_y_minus_x[Order::kX] = + -y[Order::kW] * x[Order::kX] + y[Order::kX] * x[Order::kW] - + y[Order::kY] * x[Order::kZ] + y[Order::kZ] * x[Order::kY]; + ambient_y_minus_x[Order::kY] = + -y[Order::kW] * x[Order::kY] + y[Order::kX] * x[Order::kZ] + + y[Order::kY] * x[Order::kW] - y[Order::kZ] * x[Order::kX]; + ambient_y_minus_x[Order::kZ] = + -y[Order::kW] * x[Order::kZ] - y[Order::kX] * x[Order::kY] + + y[Order::kY] * x[Order::kX] + y[Order::kZ] * x[Order::kW]; + + const double u_norm = + std::sqrt(ambient_y_minus_x[Order::kX] * ambient_y_minus_x[Order::kX] + + ambient_y_minus_x[Order::kY] * ambient_y_minus_x[Order::kY] + + ambient_y_minus_x[Order::kZ] * ambient_y_minus_x[Order::kZ]); + if (u_norm > 0.0) { + const double theta = std::atan2(u_norm, ambient_y_minus_x[Order::kW]); + y_minus_x[0] = theta * ambient_y_minus_x[Order::kX] / u_norm; + y_minus_x[1] = theta * ambient_y_minus_x[Order::kY] / u_norm; + y_minus_x[2] = theta * ambient_y_minus_x[Order::kZ] / u_norm; + } else { + y_minus_x[0] = 0.0; + y_minus_x[1] = 0.0; + y_minus_x[2] = 0.0; + } +} + +template <typename Order> +inline void QuaternionMinusJacobianImpl(const double* x, double* jacobian_ptr) { + Eigen::Map<Eigen::Matrix<double, 3, 4, Eigen::RowMajor>> jacobian( + jacobian_ptr); + + jacobian(0, Order::kW) = -x[Order::kX]; + jacobian(0, Order::kX) = x[Order::kW]; + jacobian(0, Order::kY) = -x[Order::kZ]; + jacobian(0, Order::kZ) = x[Order::kY]; + jacobian(1, Order::kW) = -x[Order::kY]; + jacobian(1, Order::kX) = x[Order::kZ]; + jacobian(1, Order::kY) = x[Order::kW]; + jacobian(1, Order::kZ) = -x[Order::kX]; + jacobian(2, Order::kW) = -x[Order::kZ]; + jacobian(2, Order::kX) = -x[Order::kY]; + jacobian(2, Order::kY) = x[Order::kX]; + jacobian(2, Order::kZ) = x[Order::kW]; +} + +} // namespace + +Manifold::~Manifold() = default; + +bool Manifold::RightMultiplyByPlusJacobian(const double* x, + const int num_rows, + const double* ambient_matrix, + double* tangent_matrix) const { + const int tangent_size = TangentSize(); + if (tangent_size == 0) { + return true; + } + + const int ambient_size = AmbientSize(); + Matrix plus_jacobian(ambient_size, tangent_size); + if (!PlusJacobian(x, plus_jacobian.data())) { + return false; + } + + MatrixRef(tangent_matrix, num_rows, tangent_size) = + ConstMatrixRef(ambient_matrix, num_rows, ambient_size) * plus_jacobian; + return true; +} + +SubsetManifold::SubsetManifold(const int size, + const std::vector<int>& constant_parameters) + + : tangent_size_(size - constant_parameters.size()), + constancy_mask_(size, false) { + if (constant_parameters.empty()) { + return; + } + + std::vector<int> constant = constant_parameters; + std::sort(constant.begin(), constant.end()); + CHECK_GE(constant.front(), 0) << "Indices indicating constant parameter must " + "be greater than equal to zero."; + CHECK_LT(constant.back(), size) + << "Indices indicating constant parameter must be less than the size " + << "of the parameter block."; + CHECK(std::adjacent_find(constant.begin(), constant.end()) == constant.end()) + << "The set of constant parameters cannot contain duplicates"; + + for (auto index : constant_parameters) { + constancy_mask_[index] = true; + } +} + +int SubsetManifold::AmbientSize() const { return constancy_mask_.size(); } + +int SubsetManifold::TangentSize() const { return tangent_size_; } + +bool SubsetManifold::Plus(const double* x, + const double* delta, + double* x_plus_delta) const { + const int ambient_size = AmbientSize(); + for (int i = 0, j = 0; i < ambient_size; ++i) { + if (constancy_mask_[i]) { + x_plus_delta[i] = x[i]; + } else { + x_plus_delta[i] = x[i] + delta[j++]; + } + } + return true; +} + +bool SubsetManifold::PlusJacobian(const double* x, + double* plus_jacobian) const { + if (tangent_size_ == 0) { + return true; + } + + const int ambient_size = AmbientSize(); + MatrixRef m(plus_jacobian, ambient_size, tangent_size_); + m.setZero(); + for (int r = 0, c = 0; r < ambient_size; ++r) { + if (!constancy_mask_[r]) { + m(r, c++) = 1.0; + } + } + return true; +} + +bool SubsetManifold::RightMultiplyByPlusJacobian(const double* x, + const int num_rows, + const double* ambient_matrix, + double* tangent_matrix) const { + if (tangent_size_ == 0) { + return true; + } + + const int ambient_size = AmbientSize(); + for (int r = 0; r < num_rows; ++r) { + for (int idx = 0, c = 0; idx < ambient_size; ++idx) { + if (!constancy_mask_[idx]) { + tangent_matrix[r * tangent_size_ + c++] = + ambient_matrix[r * ambient_size + idx]; + } + } + } + return true; +} + +bool SubsetManifold::Minus(const double* y, + const double* x, + double* y_minus_x) const { + if (tangent_size_ == 0) { + return true; + } + + const int ambient_size = AmbientSize(); + for (int i = 0, j = 0; i < ambient_size; ++i) { + if (!constancy_mask_[i]) { + y_minus_x[j++] = y[i] - x[i]; + } + } + return true; +} + +bool SubsetManifold::MinusJacobian(const double* x, + double* minus_jacobian) const { + const int ambient_size = AmbientSize(); + MatrixRef m(minus_jacobian, tangent_size_, ambient_size); + m.setZero(); + for (int c = 0, r = 0; c < ambient_size; ++c) { + if (!constancy_mask_[c]) { + m(r++, c) = 1.0; + } + } + return true; +} + +bool QuaternionManifold::Plus(const double* x, + const double* delta, + double* x_plus_delta) const { + QuaternionPlusImpl<CeresQuaternionOrder>(x, delta, x_plus_delta); + return true; +} + +bool QuaternionManifold::PlusJacobian(const double* x, double* jacobian) const { + QuaternionPlusJacobianImpl<CeresQuaternionOrder>(x, jacobian); + return true; +} + +bool QuaternionManifold::Minus(const double* y, + const double* x, + double* y_minus_x) const { + QuaternionMinusImpl<CeresQuaternionOrder>(y, x, y_minus_x); + return true; +} + +bool QuaternionManifold::MinusJacobian(const double* x, + double* jacobian) const { + QuaternionMinusJacobianImpl<CeresQuaternionOrder>(x, jacobian); + return true; +} + +bool EigenQuaternionManifold::Plus(const double* x, + const double* delta, + double* x_plus_delta) const { + QuaternionPlusImpl<EigenQuaternionOrder>(x, delta, x_plus_delta); + return true; +} + +bool EigenQuaternionManifold::PlusJacobian(const double* x, + double* jacobian) const { + QuaternionPlusJacobianImpl<EigenQuaternionOrder>(x, jacobian); + return true; +} + +bool EigenQuaternionManifold::Minus(const double* y, + const double* x, + double* y_minus_x) const { + QuaternionMinusImpl<EigenQuaternionOrder>(y, x, y_minus_x); + return true; +} + +bool EigenQuaternionManifold::MinusJacobian(const double* x, + double* jacobian) const { + QuaternionMinusJacobianImpl<EigenQuaternionOrder>(x, jacobian); + return true; +} + +} // namespace ceres diff --git a/extern/ceres/internal/ceres/manifold_adapter.h b/extern/ceres/internal/ceres/manifold_adapter.h new file mode 100644 index 00000000000..9a21456a731 --- /dev/null +++ b/extern/ceres/internal/ceres/manifold_adapter.h @@ -0,0 +1,60 @@ +#include "ceres/internal/export.h" +#include "ceres/local_parameterization.h" +#include "ceres/manifold.h" +#include "glog/logging.h" + +namespace ceres { +namespace internal { + +// Adapter to wrap LocalParameterization and make them look like Manifolds. +// +// ManifoldAdapter NEVER takes ownership of local_parameterization. +class CERES_NO_EXPORT ManifoldAdapter final : public Manifold { + public: + explicit ManifoldAdapter(const LocalParameterization* local_parameterization) + : local_parameterization_(local_parameterization) { + CHECK(local_parameterization != nullptr); + } + + bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const override { + return local_parameterization_->Plus(x, delta, x_plus_delta); + } + + bool PlusJacobian(const double* x, double* jacobian) const override { + return local_parameterization_->ComputeJacobian(x, jacobian); + } + + bool RightMultiplyByPlusJacobian(const double* x, + const int num_rows, + const double* ambient_matrix, + double* tangent_matrix) const override { + return local_parameterization_->MultiplyByJacobian( + x, num_rows, ambient_matrix, tangent_matrix); + } + + bool Minus(const double* y, const double* x, double* delta) const override { + LOG(FATAL) << "This should never be called."; + return false; + } + + bool MinusJacobian(const double* x, double* jacobian) const override { + LOG(FATAL) << "This should never be called."; + return false; + } + + int AmbientSize() const override { + return local_parameterization_->GlobalSize(); + } + + int TangentSize() const override { + return local_parameterization_->LocalSize(); + } + + private: + const LocalParameterization* local_parameterization_; +}; + +} // namespace internal +} // namespace ceres diff --git a/extern/ceres/internal/ceres/map_util.h b/extern/ceres/internal/ceres/map_util.h index 6e310f8db2d..5632c22e916 100644 --- a/extern/ceres/internal/ceres/map_util.h +++ b/extern/ceres/internal/ceres/map_util.h @@ -35,7 +35,7 @@ #include <utility> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "glog/logging.h" namespace ceres { @@ -121,7 +121,7 @@ template <class Collection> void InsertOrDie(Collection* const collection, const typename Collection::value_type::first_type& key, const typename Collection::value_type::second_type& data) { - typedef typename Collection::value_type value_type; + using value_type = typename Collection::value_type; CHECK(collection->insert(value_type(key, data)).second) << "duplicate key: " << key; } diff --git a/extern/ceres/internal/ceres/minimizer.cc b/extern/ceres/internal/ceres/minimizer.cc index b96e0c9de44..449c728774d 100644 --- a/extern/ceres/internal/ceres/minimizer.cc +++ b/extern/ceres/internal/ceres/minimizer.cc @@ -30,6 +30,8 @@ #include "ceres/minimizer.h" +#include <memory> + #include "ceres/line_search_minimizer.h" #include "ceres/trust_region_minimizer.h" #include "ceres/types.h" @@ -38,20 +40,20 @@ namespace ceres { namespace internal { -Minimizer* Minimizer::Create(MinimizerType minimizer_type) { +std::unique_ptr<Minimizer> Minimizer::Create(MinimizerType minimizer_type) { if (minimizer_type == TRUST_REGION) { - return new TrustRegionMinimizer; + return std::make_unique<TrustRegionMinimizer>(); } if (minimizer_type == LINE_SEARCH) { - return new LineSearchMinimizer; + return std::make_unique<LineSearchMinimizer>(); } LOG(FATAL) << "Unknown minimizer_type: " << minimizer_type; - return NULL; + return nullptr; } -Minimizer::~Minimizer() {} +Minimizer::~Minimizer() = default; bool Minimizer::RunCallbacks(const Minimizer::Options& options, const IterationSummary& iteration_summary, diff --git a/extern/ceres/internal/ceres/minimizer.h b/extern/ceres/internal/ceres/minimizer.h index 246550de7cd..c2c1f71df9f 100644 --- a/extern/ceres/internal/ceres/minimizer.h +++ b/extern/ceres/internal/ceres/minimizer.h @@ -35,7 +35,8 @@ #include <string> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/iteration_callback.h" #include "ceres/solver.h" @@ -49,7 +50,7 @@ class CoordinateDescentMinimizer; class LinearSolver; // Interface for non-linear least squares solvers. -class CERES_EXPORT_INTERNAL Minimizer { +class CERES_NO_EXPORT Minimizer { public: // Options struct to control the behaviour of the Minimizer. Please // see solver.h for detailed information about the meaning and @@ -178,7 +179,7 @@ class CERES_EXPORT_INTERNAL Minimizer { std::shared_ptr<CoordinateDescentMinimizer> inner_iteration_minimizer; }; - static Minimizer* Create(MinimizerType minimizer_type); + static std::unique_ptr<Minimizer> Create(MinimizerType minimizer_type); static bool RunCallbacks(const Options& options, const IterationSummary& iteration_summary, Solver::Summary* summary); @@ -195,4 +196,6 @@ class CERES_EXPORT_INTERNAL Minimizer { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_MINIMIZER_H_ diff --git a/extern/ceres/internal/ceres/normal_prior.cc b/extern/ceres/internal/ceres/normal_prior.cc index 4a62132dbda..17de40f2e77 100644 --- a/extern/ceres/internal/ceres/normal_prior.cc +++ b/extern/ceres/internal/ceres/normal_prior.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -56,7 +56,7 @@ bool NormalPrior::Evaluate(double const* const* parameters, // r = A_ * (p - b_); // The extra eval is to get around a bug in the eigen library. r = A_ * (p - b_).eval(); - if ((jacobians != NULL) && (jacobians[0] != NULL)) { + if ((jacobians != nullptr) && (jacobians[0] != nullptr)) { MatrixRef(jacobians[0], num_residuals(), parameter_block_sizes()[0]) = A_; } return true; diff --git a/extern/ceres/internal/ceres/pair_hash.h b/extern/ceres/internal/ceres/pair_hash.h index abbedccf961..83ff2b46401 100644 --- a/extern/ceres/internal/ceres/pair_hash.h +++ b/extern/ceres/internal/ceres/pair_hash.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2018 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -33,10 +33,12 @@ #ifndef CERES_INTERNAL_PAIR_HASH_H_ #define CERES_INTERNAL_PAIR_HASH_H_ +#include <cstddef> #include <cstdint> +#include <functional> #include <utility> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { diff --git a/extern/ceres/internal/ceres/parallel_for.h b/extern/ceres/internal/ceres/parallel_for.h index b64bd310650..9528c267d49 100644 --- a/extern/ceres/internal/ceres/parallel_for.h +++ b/extern/ceres/internal/ceres/parallel_for.h @@ -28,37 +28,38 @@ // // Author: vitus@google.com (Michael Vitus) -#ifndef CERES_INTERNAL_PARALLEL_FOR_ -#define CERES_INTERNAL_PARALLEL_FOR_ +#ifndef CERES_INTERNAL_PARALLEL_FOR_H_ +#define CERES_INTERNAL_PARALLEL_FOR_H_ #include <functional> #include "ceres/context_impl.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { // Returns the maximum number of threads supported by the threading backend // Ceres was compiled with. +CERES_NO_EXPORT int MaxNumThreadsAvailable(); // Execute the function for every element in the range [start, end) with at most // num_threads. It will execute all the work on the calling thread if // num_threads is 1. -CERES_EXPORT_INTERNAL void ParallelFor( - ContextImpl* context, - int start, - int end, - int num_threads, - const std::function<void(int)>& function); +CERES_NO_EXPORT void ParallelFor(ContextImpl* context, + int start, + int end, + int num_threads, + const std::function<void(int)>& function); // Execute the function for every element in the range [start, end) with at most // num_threads. It will execute all the work on the calling thread if // num_threads is 1. Each invocation of function() will be passed a thread_id // in [0, num_threads) that is guaranteed to be distinct from the value passed // to any concurrent execution of function(). -CERES_EXPORT_INTERNAL void ParallelFor( +CERES_NO_EXPORT void ParallelFor( ContextImpl* context, int start, int end, @@ -67,4 +68,6 @@ CERES_EXPORT_INTERNAL void ParallelFor( } // namespace internal } // namespace ceres +#include "ceres/internal/disable_warnings.h" + #endif // CERES_INTERNAL_PARALLEL_FOR_H_ diff --git a/extern/ceres/internal/ceres/parallel_for_cxx.cc b/extern/ceres/internal/ceres/parallel_for_cxx.cc index 4da40c01eb6..5b78db19a44 100644 --- a/extern/ceres/internal/ceres/parallel_for_cxx.cc +++ b/extern/ceres/internal/ceres/parallel_for_cxx.cc @@ -29,7 +29,7 @@ // Author: vitus@google.com (Michael Vitus) // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifdef CERES_USE_CXX_THREADS @@ -125,7 +125,7 @@ void ParallelFor(ContextImpl* context, int num_threads, const std::function<void(int)>& function) { CHECK_GT(num_threads, 0); - CHECK(context != NULL); + CHECK(context != nullptr); if (end <= start) { return; } @@ -167,7 +167,7 @@ void ParallelFor(ContextImpl* context, int num_threads, const std::function<void(int thread_id, int i)>& function) { CHECK_GT(num_threads, 0); - CHECK(context != NULL); + CHECK(context != nullptr); if (end <= start) { return; } diff --git a/extern/ceres/internal/ceres/parallel_for_nothreads.cc b/extern/ceres/internal/ceres/parallel_for_nothreads.cc index d036569fcd7..1c1871662c8 100644 --- a/extern/ceres/internal/ceres/parallel_for_nothreads.cc +++ b/extern/ceres/internal/ceres/parallel_for_nothreads.cc @@ -29,7 +29,7 @@ // Author: alexs.mac@gmail.com (Alex Stewart) // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifdef CERES_NO_THREADS @@ -47,7 +47,7 @@ void ParallelFor(ContextImpl* context, int num_threads, const std::function<void(int)>& function) { CHECK_GT(num_threads, 0); - CHECK(context != NULL); + CHECK(context != nullptr); if (end <= start) { return; } @@ -62,7 +62,7 @@ void ParallelFor(ContextImpl* context, int num_threads, const std::function<void(int thread_id, int i)>& function) { CHECK_GT(num_threads, 0); - CHECK(context != NULL); + CHECK(context != nullptr); if (end <= start) { return; } diff --git a/extern/ceres/internal/ceres/parallel_for_openmp.cc b/extern/ceres/internal/ceres/parallel_for_openmp.cc index eb9d90581ae..1d44bf9977a 100644 --- a/extern/ceres/internal/ceres/parallel_for_openmp.cc +++ b/extern/ceres/internal/ceres/parallel_for_openmp.cc @@ -29,7 +29,7 @@ // Author: vitus@google.com (Michael Vitus) // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #if defined(CERES_USE_OPENMP) @@ -50,7 +50,7 @@ void ParallelFor(ContextImpl* context, int num_threads, const std::function<void(int)>& function) { CHECK_GT(num_threads, 0); - CHECK(context != NULL); + CHECK(context != nullptr); if (end <= start) { return; } @@ -69,7 +69,7 @@ void ParallelFor(ContextImpl* context, int end, int num_threads, const std::function<void(int thread_id, int i)>& function) { - CHECK(context != NULL); + CHECK(context != nullptr); ThreadTokenProvider thread_token_provider(num_threads); ParallelFor(context, start, end, num_threads, [&](int i) { diff --git a/extern/ceres/internal/ceres/parallel_utils.h b/extern/ceres/internal/ceres/parallel_utils.h index 89d21106d74..b2d9e0da765 100644 --- a/extern/ceres/internal/ceres/parallel_utils.h +++ b/extern/ceres/internal/ceres/parallel_utils.h @@ -31,7 +31,7 @@ #ifndef CERES_INTERNAL_PARALLEL_UTILS_H_ #define CERES_INTERNAL_PARALLEL_UTILS_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -61,10 +61,10 @@ namespace internal { // }); // which in each iteration will produce i and j satisfying // 0 <= i <= j < n -CERES_EXPORT_INTERNAL void LinearIndexToUpperTriangularIndex(int k, - int n, - int* i, - int* j); +CERES_NO_EXPORT void LinearIndexToUpperTriangularIndex(int k, + int n, + int* i, + int* j); } // namespace internal } // namespace ceres diff --git a/extern/ceres/internal/ceres/parameter_block.h b/extern/ceres/internal/ceres/parameter_block.h index 88943dfbcff..a9845a3a9e3 100644 --- a/extern/ceres/internal/ceres/parameter_block.h +++ b/extern/ceres/internal/ceres/parameter_block.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2019 Google Inc. All rights reserved. +// Copyright 2021 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -40,9 +40,10 @@ #include <unordered_set> #include "ceres/array_utils.h" +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" -#include "ceres/local_parameterization.h" +#include "ceres/internal/export.h" +#include "ceres/manifold.h" #include "ceres/stringprintf.h" #include "glog/logging.h" @@ -58,12 +59,12 @@ class ResidualBlock; // methods are performance sensitive. // // The class is not thread-safe, unless only const methods are called. The -// parameter block may also hold a pointer to a local parameterization; the -// parameter block does not take ownership of this pointer, so the user is -// responsible for the proper disposal of the local parameterization. -class ParameterBlock { +// parameter block may also hold a pointer to a manifold; the parameter block +// does not take ownership of this pointer, so the user is responsible for the +// proper disposal of the manifold. +class CERES_NO_EXPORT ParameterBlock { public: - typedef std::unordered_set<ResidualBlock*> ResidualBlockSet; + using ResidualBlockSet = std::unordered_set<ResidualBlock*>; // Create a parameter block with the user state, size, and index specified. // The size is the size of the parameter block and the index is the position @@ -74,16 +75,13 @@ class ParameterBlock { state_(user_state), index_(index) {} - ParameterBlock(double* user_state, - int size, - int index, - LocalParameterization* local_parameterization) + ParameterBlock(double* user_state, int size, int index, Manifold* manifold) : user_state_(user_state), size_(size), state_(user_state), index_(index) { - if (local_parameterization != nullptr) { - SetParameterization(local_parameterization); + if (manifold != nullptr) { + SetManifold(manifold); } } @@ -98,7 +96,7 @@ class ParameterBlock { << "with user location " << user_state_; state_ = x; - return UpdateLocalParameterizationJacobian(); + return UpdatePlusJacobian(); } // Copy the current parameter state out to x. This is "GetState()" rather than @@ -114,17 +112,13 @@ class ParameterBlock { const double* state() const { return state_; } const double* user_state() const { return user_state_; } double* mutable_user_state() { return user_state_; } - const LocalParameterization* local_parameterization() const { - return local_parameterization_; - } - LocalParameterization* mutable_local_parameterization() { - return local_parameterization_; - } + const Manifold* manifold() const { return manifold_; } + Manifold* mutable_manifold() { return manifold_; } // Set this parameter block to vary or not. void SetConstant() { is_set_constant_ = true; } void SetVarying() { is_set_constant_ = false; } - bool IsConstant() const { return (is_set_constant_ || LocalSize() == 0); } + bool IsConstant() const { return (is_set_constant_ || TangentSize() == 0); } double UpperBound(int index) const { return (upper_bounds_ ? upper_bounds_[index] @@ -151,51 +145,46 @@ class ParameterBlock { int delta_offset() const { return delta_offset_; } void set_delta_offset(int delta_offset) { delta_offset_ = delta_offset; } - // Methods relating to the parameter block's parameterization. + // Methods relating to the parameter block's manifold. - // The local to global jacobian. Returns nullptr if there is no local - // parameterization for this parameter block. The returned matrix is row-major - // and has Size() rows and LocalSize() columns. - const double* LocalParameterizationJacobian() const { - return local_parameterization_jacobian_.get(); - } + // The local to global jacobian. Returns nullptr if there is no manifold for + // this parameter block. The returned matrix is row-major and has Size() rows + // and TangentSize() columns. + const double* PlusJacobian() const { return plus_jacobian_.get(); } - int LocalSize() const { - return (local_parameterization_ == nullptr) - ? size_ - : local_parameterization_->LocalSize(); + int TangentSize() const { + return (manifold_ == nullptr) ? size_ : manifold_->TangentSize(); } - // Set the parameterization. The parameter block does not take - // ownership of the parameterization. - void SetParameterization(LocalParameterization* new_parameterization) { - // Nothing to do if the new parameterization is the same as the - // old parameterization. - if (new_parameterization == local_parameterization_) { + // Set the manifold. The parameter block does not take ownership of + // the manifold. + void SetManifold(Manifold* new_manifold) { + // Nothing to do if the new manifold is the same as the old + // manifold. + if (new_manifold == manifold_) { return; } - if (new_parameterization == nullptr) { - local_parameterization_ = nullptr; + if (new_manifold == nullptr) { + manifold_ = nullptr; + plus_jacobian_ = nullptr; return; } - CHECK(new_parameterization->GlobalSize() == size_) - << "Invalid parameterization for parameter block. The parameter block " - << "has size " << size_ << " while the parameterization has a global " - << "size of " << new_parameterization->GlobalSize() << ". Did you " - << "accidentally use the wrong parameter block or parameterization?"; + CHECK_EQ(new_manifold->AmbientSize(), size_) + << "The parameter block has size = " << size_ + << " while the manifold has ambient size = " + << new_manifold->AmbientSize(); - CHECK_GE(new_parameterization->LocalSize(), 0) - << "Invalid parameterization. Parameterizations must have a " + CHECK_GE(new_manifold->TangentSize(), 0) + << "Invalid Manifold. Manifolds must have a " << "non-negative dimensional tangent space."; - local_parameterization_ = new_parameterization; - local_parameterization_jacobian_.reset( - new double[local_parameterization_->GlobalSize() * - local_parameterization_->LocalSize()]); - CHECK(UpdateLocalParameterizationJacobian()) - << "Local parameterization Jacobian computation failed for x: " + manifold_ = new_manifold; + plus_jacobian_ = std::make_unique<double[]>(manifold_->AmbientSize() * + manifold_->TangentSize()); + CHECK(UpdatePlusJacobian()) + << "Manifold::PlusJacobian computation failed for x: " << ConstVectorRef(state_, Size()).transpose(); } @@ -207,7 +196,7 @@ class ParameterBlock { } if (!upper_bounds_) { - upper_bounds_.reset(new double[size_]); + upper_bounds_ = std::make_unique<double[]>(size_); std::fill(upper_bounds_.get(), upper_bounds_.get() + size_, std::numeric_limits<double>::max()); @@ -224,7 +213,7 @@ class ParameterBlock { } if (!lower_bounds_) { - lower_bounds_.reset(new double[size_]); + lower_bounds_ = std::make_unique<double[]>(size_); std::fill(lower_bounds_.get(), lower_bounds_.get() + size_, -std::numeric_limits<double>::max()); @@ -234,11 +223,11 @@ class ParameterBlock { } // Generalization of the addition operation. This is the same as - // LocalParameterization::Plus() followed by projection onto the + // Manifold::Plus() followed by projection onto the // hyper cube implied by the bounds constraints. bool Plus(const double* x, const double* delta, double* x_plus_delta) { - if (local_parameterization_ != nullptr) { - if (!local_parameterization_->Plus(x, delta, x_plus_delta)) { + if (manifold_ != nullptr) { + if (!manifold_->Plus(x, delta, x_plus_delta)) { return false; } } else { @@ -281,7 +270,7 @@ class ParameterBlock { CHECK(residual_blocks_.get() == nullptr) << "Ceres bug: There is already a residual block collection " << "for parameter block: " << ToString(); - residual_blocks_.reset(new ResidualBlockSet); + residual_blocks_ = std::make_unique<ResidualBlockSet>(); } void AddResidualBlock(ResidualBlock* residual_block) { @@ -321,33 +310,30 @@ class ParameterBlock { } private: - bool UpdateLocalParameterizationJacobian() { - if (local_parameterization_ == nullptr) { + bool UpdatePlusJacobian() { + if (manifold_ == nullptr) { return true; } - // Update the local to global Jacobian. In some cases this is + // Update the Plus Jacobian. In some cases this is // wasted effort; if this is a bottleneck, we will find a solution // at that time. - - const int jacobian_size = Size() * LocalSize(); - InvalidateArray(jacobian_size, local_parameterization_jacobian_.get()); - if (!local_parameterization_->ComputeJacobian( - state_, local_parameterization_jacobian_.get())) { - LOG(WARNING) << "Local parameterization Jacobian computation failed" + const int jacobian_size = Size() * TangentSize(); + InvalidateArray(jacobian_size, plus_jacobian_.get()); + if (!manifold_->PlusJacobian(state_, plus_jacobian_.get())) { + LOG(WARNING) << "Manifold::PlusJacobian computation failed" "for x: " << ConstVectorRef(state_, Size()).transpose(); return false; } - if (!IsArrayValid(jacobian_size, local_parameterization_jacobian_.get())) { - LOG(WARNING) << "Local parameterization Jacobian computation returned" + if (!IsArrayValid(jacobian_size, plus_jacobian_.get())) { + LOG(WARNING) << "Manifold::PlusJacobian computation returned " << "an invalid matrix for x: " << ConstVectorRef(state_, Size()).transpose() << "\n Jacobian matrix : " - << ConstMatrixRef(local_parameterization_jacobian_.get(), - Size(), - LocalSize()); + << ConstMatrixRef( + plus_jacobian_.get(), Size(), TangentSize()); return false; } return true; @@ -356,14 +342,14 @@ class ParameterBlock { double* user_state_ = nullptr; int size_ = -1; bool is_set_constant_ = false; - LocalParameterization* local_parameterization_ = nullptr; + Manifold* manifold_ = nullptr; // The "state" of the parameter. These fields are only needed while the // solver is running. While at first glance using mutable is a bad idea, this // ends up simplifying the internals of Ceres enough to justify the potential // pitfalls of using "mutable." mutable const double* state_ = nullptr; - mutable std::unique_ptr<double[]> local_parameterization_jacobian_; + mutable std::unique_ptr<double[]> plus_jacobian_; // The index of the parameter. This is used by various other parts of Ceres to // permit switching from a ParameterBlock* to an index in another array. @@ -392,11 +378,13 @@ class ParameterBlock { std::unique_ptr<double[]> upper_bounds_; std::unique_ptr<double[]> lower_bounds_; - // Necessary so ProblemImpl can clean up the parameterizations. + // Necessary so ProblemImpl can clean up the manifolds. friend class ProblemImpl; }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_PARAMETER_BLOCK_H_ diff --git a/extern/ceres/internal/ceres/parameter_block_ordering.cc b/extern/ceres/internal/ceres/parameter_block_ordering.cc index 9899c243899..570a09c60ba 100644 --- a/extern/ceres/internal/ceres/parameter_block_ordering.cc +++ b/extern/ceres/internal/ceres/parameter_block_ordering.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -54,14 +54,14 @@ int ComputeStableSchurOrdering(const Program& program, CHECK(ordering != nullptr); ordering->clear(); EventLogger event_logger("ComputeStableSchurOrdering"); - std::unique_ptr<Graph<ParameterBlock*>> graph(CreateHessianGraph(program)); + auto graph = CreateHessianGraph(program); event_logger.AddEvent("CreateHessianGraph"); const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks(); const std::unordered_set<ParameterBlock*>& vertices = graph->vertices(); - for (int i = 0; i < parameter_blocks.size(); ++i) { - if (vertices.count(parameter_blocks[i]) > 0) { - ordering->push_back(parameter_blocks[i]); + for (auto* parameter_block : parameter_blocks) { + if (vertices.count(parameter_block) > 0) { + ordering->push_back(parameter_block); } } event_logger.AddEvent("Preordering"); @@ -70,8 +70,7 @@ int ComputeStableSchurOrdering(const Program& program, event_logger.AddEvent("StableIndependentSet"); // Add the excluded blocks to back of the ordering vector. - for (int i = 0; i < parameter_blocks.size(); ++i) { - ParameterBlock* parameter_block = parameter_blocks[i]; + for (auto* parameter_block : parameter_blocks) { if (parameter_block->IsConstant()) { ordering->push_back(parameter_block); } @@ -86,13 +85,12 @@ int ComputeSchurOrdering(const Program& program, CHECK(ordering != nullptr); ordering->clear(); - std::unique_ptr<Graph<ParameterBlock*>> graph(CreateHessianGraph(program)); + auto graph = CreateHessianGraph(program); int independent_set_size = IndependentSetOrdering(*graph, ordering); const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks(); // Add the excluded blocks to back of the ordering vector. - for (int i = 0; i < parameter_blocks.size(); ++i) { - ParameterBlock* parameter_block = parameter_blocks[i]; + for (auto* parameter_block : parameter_blocks) { if (parameter_block->IsConstant()) { ordering->push_back(parameter_block); } @@ -106,7 +104,7 @@ void ComputeRecursiveIndependentSetOrdering(const Program& program, CHECK(ordering != nullptr); ordering->Clear(); const vector<ParameterBlock*> parameter_blocks = program.parameter_blocks(); - std::unique_ptr<Graph<ParameterBlock*>> graph(CreateHessianGraph(program)); + auto graph = CreateHessianGraph(program); int num_covered = 0; int round = 0; @@ -124,20 +122,19 @@ void ComputeRecursiveIndependentSetOrdering(const Program& program, } } -Graph<ParameterBlock*>* CreateHessianGraph(const Program& program) { - Graph<ParameterBlock*>* graph = new Graph<ParameterBlock*>; +std::unique_ptr<Graph<ParameterBlock*>> CreateHessianGraph( + const Program& program) { + auto graph = std::make_unique<Graph<ParameterBlock*>>(); CHECK(graph != nullptr); const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks(); - for (int i = 0; i < parameter_blocks.size(); ++i) { - ParameterBlock* parameter_block = parameter_blocks[i]; + for (auto* parameter_block : parameter_blocks) { if (!parameter_block->IsConstant()) { graph->AddVertex(parameter_block); } } const vector<ResidualBlock*>& residual_blocks = program.residual_blocks(); - for (int i = 0; i < residual_blocks.size(); ++i) { - const ResidualBlock* residual_block = residual_blocks[i]; + for (auto* residual_block : residual_blocks) { const int num_parameter_blocks = residual_block->NumParameterBlocks(); ParameterBlock* const* parameter_blocks = residual_block->parameter_blocks(); @@ -163,7 +160,7 @@ void OrderingToGroupSizes(const ParameterBlockOrdering* ordering, vector<int>* group_sizes) { CHECK(group_sizes != nullptr); group_sizes->clear(); - if (ordering == NULL) { + if (ordering == nullptr) { return; } diff --git a/extern/ceres/internal/ceres/parameter_block_ordering.h b/extern/ceres/internal/ceres/parameter_block_ordering.h index 82ab75dc6dc..f9a447adf87 100644 --- a/extern/ceres/internal/ceres/parameter_block_ordering.h +++ b/extern/ceres/internal/ceres/parameter_block_ordering.h @@ -31,10 +31,12 @@ #ifndef CERES_INTERNAL_PARAMETER_BLOCK_ORDERING_H_ #define CERES_INTERNAL_PARAMETER_BLOCK_ORDERING_H_ +#include <memory> #include <vector> #include "ceres/graph.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/ordered_groups.h" #include "ceres/types.h" @@ -57,20 +59,20 @@ class ParameterBlock; // ordering = [independent set, // complement of the independent set, // fixed blocks] -CERES_EXPORT_INTERNAL int ComputeSchurOrdering( +CERES_NO_EXPORT int ComputeSchurOrdering( const Program& program, std::vector<ParameterBlock*>* ordering); // Same as above, except that ties while computing the independent set // ordering are resolved in favour of the order in which the parameter // blocks occur in the program. -CERES_EXPORT_INTERNAL int ComputeStableSchurOrdering( +CERES_NO_EXPORT int ComputeStableSchurOrdering( const Program& program, std::vector<ParameterBlock*>* ordering); // Use an approximate independent set ordering to decompose the // parameter blocks of a problem in a sequence of independent // sets. The ordering covers all the non-constant parameter blocks in // the program. -CERES_EXPORT_INTERNAL void ComputeRecursiveIndependentSetOrdering( +CERES_NO_EXPORT void ComputeRecursiveIndependentSetOrdering( const Program& program, ParameterBlockOrdering* ordering); // Builds a graph on the parameter blocks of a Problem, whose @@ -78,15 +80,17 @@ CERES_EXPORT_INTERNAL void ComputeRecursiveIndependentSetOrdering( // vertex corresponds to a parameter block in the Problem except for // parameter blocks that are marked constant. An edge connects two // parameter blocks, if they co-occur in a residual block. -CERES_EXPORT_INTERNAL Graph<ParameterBlock*>* CreateHessianGraph( +CERES_NO_EXPORT std::unique_ptr<Graph<ParameterBlock*>> CreateHessianGraph( const Program& program); // Iterate over each of the groups in order of their priority and fill // summary with their sizes. -CERES_EXPORT_INTERNAL void OrderingToGroupSizes( +CERES_NO_EXPORT void OrderingToGroupSizes( const ParameterBlockOrdering* ordering, std::vector<int>* group_sizes); } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_PARAMETER_BLOCK_ORDERING_H_ diff --git a/extern/ceres/internal/ceres/partitioned_matrix_view.cc b/extern/ceres/internal/ceres/partitioned_matrix_view.cc index b67bc905d15..d38f30a09d7 100644 --- a/extern/ceres/internal/ceres/partitioned_matrix_view.cc +++ b/extern/ceres/internal/ceres/partitioned_matrix_view.cc @@ -39,143 +39,147 @@ // // This file is generated using generate_template_specializations.py. +#include <memory> + #include "ceres/linear_solver.h" #include "ceres/partitioned_matrix_view.h" namespace ceres { namespace internal { -PartitionedMatrixViewBase* PartitionedMatrixViewBase::Create( +PartitionedMatrixViewBase::~PartitionedMatrixViewBase() = default; + +std::unique_ptr<PartitionedMatrixViewBase> PartitionedMatrixViewBase::Create( const LinearSolver::Options& options, const BlockSparseMatrix& matrix) { #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION if ((options.row_block_size == 2) && (options.e_block_size == 2) && (options.f_block_size == 2)) { - return new PartitionedMatrixView<2, 2, 2>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,2, 2>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 2) && (options.f_block_size == 3)) { - return new PartitionedMatrixView<2, 2, 3>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,2, 3>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 2) && (options.f_block_size == 4)) { - return new PartitionedMatrixView<2, 2, 4>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,2, 4>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 2)) { - return new PartitionedMatrixView<2, 2, Eigen::Dynamic>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,2, Eigen::Dynamic>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 3) && (options.f_block_size == 3)) { - return new PartitionedMatrixView<2, 3, 3>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,3, 3>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 3) && (options.f_block_size == 4)) { - return new PartitionedMatrixView<2, 3, 4>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,3, 4>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 3) && (options.f_block_size == 6)) { - return new PartitionedMatrixView<2, 3, 6>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,3, 6>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 3) && (options.f_block_size == 9)) { - return new PartitionedMatrixView<2, 3, 9>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,3, 9>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 3)) { - return new PartitionedMatrixView<2, 3, Eigen::Dynamic>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,3, Eigen::Dynamic>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 3)) { - return new PartitionedMatrixView<2, 4, 3>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,4, 3>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 4)) { - return new PartitionedMatrixView<2, 4, 4>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,4, 4>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 6)) { - return new PartitionedMatrixView<2, 4, 6>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,4, 6>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 8)) { - return new PartitionedMatrixView<2, 4, 8>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,4, 8>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 9)) { - return new PartitionedMatrixView<2, 4, 9>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,4, 9>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 4)) { - return new PartitionedMatrixView<2, 4, Eigen::Dynamic>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,4, Eigen::Dynamic>>( + matrix, options.elimination_groups[0]); } if (options.row_block_size == 2) { - return new PartitionedMatrixView<2, Eigen::Dynamic, Eigen::Dynamic>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,Eigen::Dynamic, Eigen::Dynamic>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 3) && (options.e_block_size == 3) && (options.f_block_size == 3)) { - return new PartitionedMatrixView<3, 3, 3>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<3,3, 3>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 4) && (options.e_block_size == 4) && (options.f_block_size == 2)) { - return new PartitionedMatrixView<4, 4, 2>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<4,4, 2>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 4) && (options.e_block_size == 4) && (options.f_block_size == 3)) { - return new PartitionedMatrixView<4, 4, 3>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<4,4, 3>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 4) && (options.e_block_size == 4) && (options.f_block_size == 4)) { - return new PartitionedMatrixView<4, 4, 4>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<4,4, 4>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 4) && (options.e_block_size == 4)) { - return new PartitionedMatrixView<4, 4, Eigen::Dynamic>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<4,4, Eigen::Dynamic>>( + matrix, options.elimination_groups[0]); } #endif VLOG(1) << "Template specializations not found for <" << options.row_block_size << "," << options.e_block_size << "," << options.f_block_size << ">"; - return new PartitionedMatrixView<Eigen::Dynamic, - Eigen::Dynamic, - Eigen::Dynamic>( + return std::make_unique<PartitionedMatrixView<Eigen::Dynamic, + Eigen::Dynamic, + Eigen::Dynamic>>( matrix, options.elimination_groups[0]); }; diff --git a/extern/ceres/internal/ceres/partitioned_matrix_view.h b/extern/ceres/internal/ceres/partitioned_matrix_view.h index 9f204ee1fdd..5623d3b6bca 100644 --- a/extern/ceres/internal/ceres/partitioned_matrix_view.h +++ b/extern/ceres/internal/ceres/partitioned_matrix_view.h @@ -38,11 +38,14 @@ #include <algorithm> #include <cstring> +#include <memory> #include <vector> #include "ceres/block_structure.h" +#include "ceres/internal/config.h" +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" #include "ceres/small_blas.h" #include "glog/logging.h" @@ -60,9 +63,9 @@ namespace internal { // block structure of the matrix does not satisfy the requirements of // the Schur complement solver it will result in unpredictable and // wrong output. -class CERES_EXPORT_INTERNAL PartitionedMatrixViewBase { +class CERES_NO_EXPORT PartitionedMatrixViewBase { public: - virtual ~PartitionedMatrixViewBase() {} + virtual ~PartitionedMatrixViewBase(); // y += E'x virtual void LeftMultiplyE(const double* x, double* y) const = 0; @@ -77,11 +80,11 @@ class CERES_EXPORT_INTERNAL PartitionedMatrixViewBase { virtual void RightMultiplyF(const double* x, double* y) const = 0; // Create and return the block diagonal of the matrix E'E. - virtual BlockSparseMatrix* CreateBlockDiagonalEtE() const = 0; + virtual std::unique_ptr<BlockSparseMatrix> CreateBlockDiagonalEtE() const = 0; // Create and return the block diagonal of the matrix F'F. Caller // owns the result. - virtual BlockSparseMatrix* CreateBlockDiagonalFtF() const = 0; + virtual std::unique_ptr<BlockSparseMatrix> CreateBlockDiagonalFtF() const = 0; // Compute the block diagonal of the matrix E'E and store it in // block_diagonal. The matrix block_diagonal is expected to have a @@ -108,26 +111,26 @@ class CERES_EXPORT_INTERNAL PartitionedMatrixViewBase { virtual int num_cols() const = 0; // clang-format on - static PartitionedMatrixViewBase* Create(const LinearSolver::Options& options, - const BlockSparseMatrix& matrix); + static std::unique_ptr<PartitionedMatrixViewBase> Create( + const LinearSolver::Options& options, const BlockSparseMatrix& matrix); }; template <int kRowBlockSize = Eigen::Dynamic, int kEBlockSize = Eigen::Dynamic, int kFBlockSize = Eigen::Dynamic> -class PartitionedMatrixView : public PartitionedMatrixViewBase { +class CERES_NO_EXPORT PartitionedMatrixView final + : public PartitionedMatrixViewBase { public: // matrix = [E F], where the matrix E contains the first // num_col_blocks_a column blocks. PartitionedMatrixView(const BlockSparseMatrix& matrix, int num_col_blocks_e); - virtual ~PartitionedMatrixView(); void LeftMultiplyE(const double* x, double* y) const final; void LeftMultiplyF(const double* x, double* y) const final; void RightMultiplyE(const double* x, double* y) const final; void RightMultiplyF(const double* x, double* y) const final; - BlockSparseMatrix* CreateBlockDiagonalEtE() const final; - BlockSparseMatrix* CreateBlockDiagonalFtF() const final; + std::unique_ptr<BlockSparseMatrix> CreateBlockDiagonalEtE() const final; + std::unique_ptr<BlockSparseMatrix> CreateBlockDiagonalFtF() const final; void UpdateBlockDiagonalEtE(BlockSparseMatrix* block_diagonal) const final; void UpdateBlockDiagonalFtF(BlockSparseMatrix* block_diagonal) const final; // clang-format off @@ -140,8 +143,8 @@ class PartitionedMatrixView : public PartitionedMatrixViewBase { // clang-format on private: - BlockSparseMatrix* CreateBlockDiagonalMatrixLayout(int start_col_block, - int end_col_block) const; + std::unique_ptr<BlockSparseMatrix> CreateBlockDiagonalMatrixLayout( + int start_col_block, int end_col_block) const; const BlockSparseMatrix& matrix_; int num_row_blocks_e_; @@ -154,4 +157,6 @@ class PartitionedMatrixView : public PartitionedMatrixViewBase { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_PARTITIONED_MATRIX_VIEW_H_ diff --git a/extern/ceres/internal/ceres/partitioned_matrix_view_impl.h b/extern/ceres/internal/ceres/partitioned_matrix_view_impl.h index 0b6a57fb9f2..2e818caa6ef 100644 --- a/extern/ceres/internal/ceres/partitioned_matrix_view_impl.h +++ b/extern/ceres/internal/ceres/partitioned_matrix_view_impl.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -30,6 +30,7 @@ #include <algorithm> #include <cstring> +#include <memory> #include <vector> #include "ceres/block_sparse_matrix.h" @@ -57,8 +58,8 @@ PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: // e_blocks. For a definition of what an e_block is, please see // explicit_schur_complement_solver.h num_row_blocks_e_ = 0; - for (int r = 0; r < bs->rows.size(); ++r) { - const std::vector<Cell>& cells = bs->rows[r].cells; + for (const auto& row : bs->rows) { + const std::vector<Cell>& cells = row.cells; if (cells[0].block_id < num_col_blocks_e_) { ++num_row_blocks_e_; } @@ -80,10 +81,6 @@ PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: CHECK_EQ(num_cols_e_ + num_cols_f_, matrix_.num_cols()); } -template <int kRowBlockSize, int kEBlockSize, int kFBlockSize> -PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: - ~PartitionedMatrixView() {} - // The next four methods don't seem to be particularly cache // friendly. This is an artifact of how the BlockStructure of the // input matrix is constructed. These methods will benefit from @@ -145,13 +142,13 @@ void PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: const int row_block_pos = bs->rows[r].block.position; const int row_block_size = bs->rows[r].block.size; const std::vector<Cell>& cells = bs->rows[r].cells; - for (int c = 0; c < cells.size(); ++c) { - const int col_block_id = cells[c].block_id; + for (const auto& cell : cells) { + const int col_block_id = cell.block_id; const int col_block_pos = bs->cols[col_block_id].position; const int col_block_size = bs->cols[col_block_id].size; // clang-format off MatrixVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>( - values + cells[c].position, row_block_size, col_block_size, + values + cell.position, row_block_size, col_block_size, x + col_block_pos - num_cols_e_, y + row_block_pos); // clang-format on @@ -215,13 +212,13 @@ void PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: const int row_block_pos = bs->rows[r].block.position; const int row_block_size = bs->rows[r].block.size; const std::vector<Cell>& cells = bs->rows[r].cells; - for (int c = 0; c < cells.size(); ++c) { - const int col_block_id = cells[c].block_id; + for (const auto& cell : cells) { + const int col_block_id = cell.block_id; const int col_block_pos = bs->cols[col_block_id].position; const int col_block_size = bs->cols[col_block_id].size; // clang-format off MatrixTransposeVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>( - values + cells[c].position, row_block_size, col_block_size, + values + cell.position, row_block_size, col_block_size, x + row_block_pos, y + col_block_pos - num_cols_e_); // clang-format on @@ -235,13 +232,12 @@ void PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: // and return a BlockSparseMatrix with the this block structure. The // caller owns the result. template <int kRowBlockSize, int kEBlockSize, int kFBlockSize> -BlockSparseMatrix* +std::unique_ptr<BlockSparseMatrix> PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: CreateBlockDiagonalMatrixLayout(int start_col_block, int end_col_block) const { const CompressedRowBlockStructure* bs = matrix_.block_structure(); - CompressedRowBlockStructure* block_diagonal_structure = - new CompressedRowBlockStructure; + auto* block_diagonal_structure = new CompressedRowBlockStructure; int block_position = 0; int diagonal_cell_position = 0; @@ -250,16 +246,16 @@ PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: // each column block. for (int c = start_col_block; c < end_col_block; ++c) { const Block& block = bs->cols[c]; - block_diagonal_structure->cols.push_back(Block()); + block_diagonal_structure->cols.emplace_back(); Block& diagonal_block = block_diagonal_structure->cols.back(); diagonal_block.size = block.size; diagonal_block.position = block_position; - block_diagonal_structure->rows.push_back(CompressedRow()); + block_diagonal_structure->rows.emplace_back(); CompressedRow& row = block_diagonal_structure->rows.back(); row.block = diagonal_block; - row.cells.push_back(Cell()); + row.cells.emplace_back(); Cell& cell = row.cells.back(); cell.block_id = c - start_col_block; cell.position = diagonal_cell_position; @@ -270,28 +266,27 @@ PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: // Build a BlockSparseMatrix with the just computed block // structure. - return new BlockSparseMatrix(block_diagonal_structure); + return std::make_unique<BlockSparseMatrix>(block_diagonal_structure); } template <int kRowBlockSize, int kEBlockSize, int kFBlockSize> -BlockSparseMatrix* PartitionedMatrixView<kRowBlockSize, - kEBlockSize, - kFBlockSize>::CreateBlockDiagonalEtE() - const { - BlockSparseMatrix* block_diagonal = +std::unique_ptr<BlockSparseMatrix> +PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: + CreateBlockDiagonalEtE() const { + std::unique_ptr<BlockSparseMatrix> block_diagonal = CreateBlockDiagonalMatrixLayout(0, num_col_blocks_e_); - UpdateBlockDiagonalEtE(block_diagonal); + UpdateBlockDiagonalEtE(block_diagonal.get()); return block_diagonal; } template <int kRowBlockSize, int kEBlockSize, int kFBlockSize> -BlockSparseMatrix* PartitionedMatrixView<kRowBlockSize, - kEBlockSize, - kFBlockSize>::CreateBlockDiagonalFtF() - const { - BlockSparseMatrix* block_diagonal = CreateBlockDiagonalMatrixLayout( - num_col_blocks_e_, num_col_blocks_e_ + num_col_blocks_f_); - UpdateBlockDiagonalFtF(block_diagonal); +std::unique_ptr<BlockSparseMatrix> +PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: + CreateBlockDiagonalFtF() const { + std::unique_ptr<BlockSparseMatrix> block_diagonal = + CreateBlockDiagonalMatrixLayout(num_col_blocks_e_, + num_col_blocks_e_ + num_col_blocks_f_); + UpdateBlockDiagonalFtF(block_diagonal.get()); return block_diagonal; } @@ -366,8 +361,8 @@ void PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: for (int r = num_row_blocks_e_; r < bs->rows.size(); ++r) { const int row_block_size = bs->rows[r].block.size; const std::vector<Cell>& cells = bs->rows[r].cells; - for (int c = 0; c < cells.size(); ++c) { - const int col_block_id = cells[c].block_id; + for (const auto& cell : cells) { + const int col_block_id = cell.block_id; const int col_block_size = bs->cols[col_block_id].size; const int diagonal_block_id = col_block_id - num_col_blocks_e_; const int cell_position = @@ -376,8 +371,8 @@ void PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: // clang-format off MatrixTransposeMatrixMultiply <Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic, 1>( - values + cells[c].position, row_block_size, col_block_size, - values + cells[c].position, row_block_size, col_block_size, + values + cell.position, row_block_size, col_block_size, + values + cell.position, row_block_size, col_block_size, block_diagonal->mutable_values() + cell_position, 0, 0, col_block_size, col_block_size); // clang-format on diff --git a/extern/ceres/internal/ceres/partitioned_matrix_view_template.py b/extern/ceres/internal/ceres/partitioned_matrix_view_template.py deleted file mode 100644 index 05a25bf8335..00000000000 --- a/extern/ceres/internal/ceres/partitioned_matrix_view_template.py +++ /dev/null @@ -1,151 +0,0 @@ -# Ceres Solver - A fast non-linear least squares minimizer -# Copyright 2015 Google Inc. All rights reserved. -# http://ceres-solver.org/ -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Google Inc. nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# Author: sameeragarwal@google.com (Sameer Agarwal) -# -# Script for explicitly generating template specialization of the -# PartitionedMatrixView class. Explicitly generating these -# instantiations in separate .cc files breaks the compilation into -# separate compilation unit rather than one large cc file. -# -# This script creates two sets of files. -# -# 1. partitioned_matrix_view_x_x_x.cc -# where the x indicates the template parameters and -# -# 2. partitioned_matrix_view.cc -# -# that contains a factory function for instantiating these classes -# based on runtime parameters. -# -# The list of tuples, specializations indicates the set of -# specializations that is generated. - -HEADER = """// Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2017 Google Inc. All rights reserved. -// http://ceres-solver.org/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Author: sameeragarwal@google.com (Sameer Agarwal) -// -// Template specialization of PartitionedMatrixView. -// -// ======================================== -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -//========================================= -// -// This file is generated using generate_template_specializations.py. -""" - -DYNAMIC_FILE = """ -#include "ceres/partitioned_matrix_view_impl.h" - -namespace ceres { -namespace internal { - -template class PartitionedMatrixView<%s, - %s, - %s>; - -} // namespace internal -} // namespace ceres -""" - -SPECIALIZATION_FILE = """ -// This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" - -#ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION - -#include "ceres/partitioned_matrix_view_impl.h" - -namespace ceres { -namespace internal { - -template class PartitionedMatrixView<%s, %s, %s>; - -} // namespace internal -} // namespace ceres - -#endif // CERES_RESTRICT_SCHUR_SPECIALIZATION -""" - -FACTORY_FILE_HEADER = """ -#include "ceres/linear_solver.h" -#include "ceres/partitioned_matrix_view.h" - -namespace ceres { -namespace internal { - -PartitionedMatrixViewBase* PartitionedMatrixViewBase::Create( - const LinearSolver::Options& options, const BlockSparseMatrix& matrix) { -#ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION -""" -FACTORY = """ return new PartitionedMatrixView<%s, %s, %s>(matrix, - options.elimination_groups[0]);""" - -FACTORY_FOOTER = """ -#endif - VLOG(1) << "Template specializations not found for <" - << options.row_block_size << "," << options.e_block_size << "," - << options.f_block_size << ">"; - return new PartitionedMatrixView<Eigen::Dynamic, - Eigen::Dynamic, - Eigen::Dynamic>( - matrix, options.elimination_groups[0]); -}; - -} // namespace internal -} // namespace ceres -""" diff --git a/extern/ceres/internal/ceres/polynomial.cc b/extern/ceres/internal/ceres/polynomial.cc index 20812f4de81..96267aae97f 100644 --- a/extern/ceres/internal/ceres/polynomial.cc +++ b/extern/ceres/internal/ceres/polynomial.cc @@ -37,7 +37,7 @@ #include "Eigen/Dense" #include "ceres/function_sample.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "glog/logging.h" namespace ceres { @@ -128,12 +128,12 @@ void FindLinearPolynomialRoots(const Vector& polynomial, Vector* real, Vector* imaginary) { CHECK_EQ(polynomial.size(), 2); - if (real != NULL) { + if (real != nullptr) { real->resize(1); (*real)(0) = -polynomial(1) / polynomial(0); } - if (imaginary != NULL) { + if (imaginary != nullptr) { imaginary->setZero(1); } } @@ -147,16 +147,16 @@ void FindQuadraticPolynomialRoots(const Vector& polynomial, const double c = polynomial(2); const double D = b * b - 4 * a * c; const double sqrt_D = sqrt(fabs(D)); - if (real != NULL) { + if (real != nullptr) { real->setZero(2); } - if (imaginary != NULL) { + if (imaginary != nullptr) { imaginary->setZero(2); } // Real roots. if (D >= 0) { - if (real != NULL) { + if (real != nullptr) { // Stable quadratic roots according to BKP Horn. // http://people.csail.mit.edu/bkph/articles/Quadratics.pdf if (b >= 0) { @@ -171,11 +171,11 @@ void FindQuadraticPolynomialRoots(const Vector& polynomial, } // Use the normal quadratic formula for the complex case. - if (real != NULL) { + if (real != nullptr) { (*real)(0) = -b / (2.0 * a); (*real)(1) = -b / (2.0 * a); } - if (imaginary != NULL) { + if (imaginary != nullptr) { (*imaginary)(0) = sqrt_D / (2.0 * a); (*imaginary)(1) = -sqrt_D / (2.0 * a); } @@ -240,14 +240,14 @@ bool FindPolynomialRoots(const Vector& polynomial_in, } // Output roots - if (real != NULL) { + if (real != nullptr) { *real = solver.eigenvalues().real(); } else { - LOG(WARNING) << "NULL pointer passed as real argument to " + LOG(WARNING) << "nullptr pointer passed as real argument to " << "FindPolynomialRoots. Real parts of the roots will not " << "be returned."; } - if (imaginary != NULL) { + if (imaginary != nullptr) { *imaginary = solver.eigenvalues().imag(); } return true; @@ -304,7 +304,7 @@ void MinimizePolynomial(const Vector& polynomial, const Vector derivative = DifferentiatePolynomial(polynomial); Vector roots_real; - if (!FindPolynomialRoots(derivative, &roots_real, NULL)) { + if (!FindPolynomialRoots(derivative, &roots_real, nullptr)) { LOG(WARNING) << "Unable to find the critical points of " << "the interpolating polynomial."; return; @@ -376,8 +376,7 @@ void MinimizeInterpolatingPolynomial(const vector<FunctionSample>& samples, double* optimal_value) { const Vector polynomial = FindInterpolatingPolynomial(samples); MinimizePolynomial(polynomial, x_min, x_max, optimal_x, optimal_value); - for (int i = 0; i < samples.size(); ++i) { - const FunctionSample& sample = samples[i]; + for (const auto& sample : samples) { if ((sample.x < x_min) || (sample.x > x_max)) { continue; } diff --git a/extern/ceres/internal/ceres/polynomial.h b/extern/ceres/internal/ceres/polynomial.h index 20071f2c693..3ca753c4618 100644 --- a/extern/ceres/internal/ceres/polynomial.h +++ b/extern/ceres/internal/ceres/polynomial.h @@ -34,8 +34,9 @@ #include <vector> +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -49,6 +50,7 @@ struct FunctionSample; // and are given by a vector of coefficients of size N + 1. // Evaluate the polynomial at x using the Horner scheme. +CERES_NO_EXPORT inline double EvaluatePolynomial(const Vector& polynomial, double x) { double v = 0.0; for (int i = 0; i < polynomial.size(); ++i) { @@ -64,15 +66,16 @@ inline double EvaluatePolynomial(const Vector& polynomial, double x) { // Failure indicates that the polynomial is invalid (of size 0) or // that the eigenvalues of the companion matrix could not be computed. // On failure, a more detailed message will be written to LOG(ERROR). -// If real is not NULL, the real parts of the roots will be returned in it. -// Likewise, if imaginary is not NULL, imaginary parts will be returned in it. -CERES_EXPORT_INTERNAL bool FindPolynomialRoots(const Vector& polynomial, - Vector* real, - Vector* imaginary); +// If real is not nullptr, the real parts of the roots will be returned in it. +// Likewise, if imaginary is not nullptr, imaginary parts will be returned in +// it. +CERES_NO_EXPORT bool FindPolynomialRoots(const Vector& polynomial, + Vector* real, + Vector* imaginary); // Return the derivative of the given polynomial. It is assumed that // the input polynomial is at least of degree zero. -CERES_EXPORT_INTERNAL Vector DifferentiatePolynomial(const Vector& polynomial); +CERES_NO_EXPORT Vector DifferentiatePolynomial(const Vector& polynomial); // Find the minimum value of the polynomial in the interval [x_min, // x_max]. The minimum is obtained by computing all the roots of the @@ -80,11 +83,11 @@ CERES_EXPORT_INTERNAL Vector DifferentiatePolynomial(const Vector& polynomial); // interval [x_min, x_max] are considered as well as the end points // x_min and x_max. Since polynomials are differentiable functions, // this ensures that the true minimum is found. -CERES_EXPORT_INTERNAL void MinimizePolynomial(const Vector& polynomial, - double x_min, - double x_max, - double* optimal_x, - double* optimal_value); +CERES_NO_EXPORT void MinimizePolynomial(const Vector& polynomial, + double x_min, + double x_max, + double* optimal_x, + double* optimal_value); // Given a set of function value and/or gradient samples, find a // polynomial whose value and gradients are exactly equal to the ones @@ -97,7 +100,7 @@ CERES_EXPORT_INTERNAL void MinimizePolynomial(const Vector& polynomial, // Of course its possible to sample a polynomial any number of times, // in which case, generally speaking the spurious higher order // coefficients will be zero. -CERES_EXPORT_INTERNAL Vector +CERES_NO_EXPORT Vector FindInterpolatingPolynomial(const std::vector<FunctionSample>& samples); // Interpolate the function described by samples with a polynomial, @@ -106,7 +109,7 @@ FindInterpolatingPolynomial(const std::vector<FunctionSample>& samples); // finding algorithms may fail due to numerical difficulties. But the // function is guaranteed to return its best guess of an answer, by // considering the samples and the end points as possible solutions. -CERES_EXPORT_INTERNAL void MinimizeInterpolatingPolynomial( +CERES_NO_EXPORT void MinimizeInterpolatingPolynomial( const std::vector<FunctionSample>& samples, double x_min, double x_max, @@ -116,4 +119,6 @@ CERES_EXPORT_INTERNAL void MinimizeInterpolatingPolynomial( } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_POLYNOMIAL_SOLVER_H_ diff --git a/extern/ceres/internal/ceres/preconditioner.cc b/extern/ceres/internal/ceres/preconditioner.cc index 69ba04db8f5..17b9629cf94 100644 --- a/extern/ceres/internal/ceres/preconditioner.cc +++ b/extern/ceres/internal/ceres/preconditioner.cc @@ -35,7 +35,7 @@ namespace ceres { namespace internal { -Preconditioner::~Preconditioner() {} +Preconditioner::~Preconditioner() = default; PreconditionerType Preconditioner::PreconditionerForZeroEBlocks( PreconditionerType preconditioner_type) { @@ -53,7 +53,8 @@ SparseMatrixPreconditionerWrapper::SparseMatrixPreconditionerWrapper( CHECK(matrix != nullptr); } -SparseMatrixPreconditionerWrapper::~SparseMatrixPreconditionerWrapper() {} +SparseMatrixPreconditionerWrapper::~SparseMatrixPreconditionerWrapper() = + default; bool SparseMatrixPreconditionerWrapper::UpdateImpl(const SparseMatrix& A, const double* D) { diff --git a/extern/ceres/internal/ceres/preconditioner.h b/extern/ceres/internal/ceres/preconditioner.h index dd843b01ce3..6433cc7dd38 100644 --- a/extern/ceres/internal/ceres/preconditioner.h +++ b/extern/ceres/internal/ceres/preconditioner.h @@ -36,7 +36,8 @@ #include "ceres/casts.h" #include "ceres/compressed_row_sparse_matrix.h" #include "ceres/context_impl.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/linear_operator.h" #include "ceres/sparse_matrix.h" #include "ceres/types.h" @@ -47,7 +48,7 @@ namespace internal { class BlockSparseMatrix; class SparseMatrix; -class CERES_EXPORT_INTERNAL Preconditioner : public LinearOperator { +class CERES_NO_EXPORT Preconditioner : public LinearOperator { public: struct Options { PreconditionerType type = JACOBI; @@ -115,7 +116,7 @@ class CERES_EXPORT_INTERNAL Preconditioner : public LinearOperator { static PreconditionerType PreconditionerForZeroEBlocks( PreconditionerType preconditioner_type); - virtual ~Preconditioner(); + ~Preconditioner() override; // Update the numerical value of the preconditioner for the linear // system: @@ -126,7 +127,7 @@ class CERES_EXPORT_INTERNAL Preconditioner : public LinearOperator { // for some vector b. It is important that the matrix A have the // same block structure as the one used to construct this object. // - // D can be NULL, in which case its interpreted as a diagonal matrix + // D can be nullptr, in which case its interpreted as a diagonal matrix // of size zero. virtual bool Update(const LinearOperator& A, const double* D) = 0; @@ -147,9 +148,8 @@ class CERES_EXPORT_INTERNAL Preconditioner : public LinearOperator { // other preconditioners that depend on the particular matrix layout of // the underlying linear operator. template <typename MatrixType> -class TypedPreconditioner : public Preconditioner { +class CERES_NO_EXPORT TypedPreconditioner : public Preconditioner { public: - virtual ~TypedPreconditioner() {} bool Update(const LinearOperator& A, const double* D) final { return UpdateImpl(*down_cast<const MatrixType*>(&A), D); } @@ -161,28 +161,31 @@ class TypedPreconditioner : public Preconditioner { // Preconditioners that depend on access to the low level structure // of a SparseMatrix. // clang-format off -typedef TypedPreconditioner<SparseMatrix> SparseMatrixPreconditioner; -typedef TypedPreconditioner<BlockSparseMatrix> BlockSparseMatrixPreconditioner; -typedef TypedPreconditioner<CompressedRowSparseMatrix> CompressedRowSparseMatrixPreconditioner; +using SparseMatrixPreconditioner = TypedPreconditioner<SparseMatrix>; +using BlockSparseMatrixPreconditioner = TypedPreconditioner<BlockSparseMatrix>; +using CompressedRowSparseMatrixPreconditioner = TypedPreconditioner<CompressedRowSparseMatrix>; // clang-format on // Wrap a SparseMatrix object as a preconditioner. -class SparseMatrixPreconditionerWrapper : public SparseMatrixPreconditioner { +class CERES_NO_EXPORT SparseMatrixPreconditionerWrapper final + : public SparseMatrixPreconditioner { public: // Wrapper does NOT take ownership of the matrix pointer. explicit SparseMatrixPreconditionerWrapper(const SparseMatrix* matrix); - virtual ~SparseMatrixPreconditionerWrapper(); + ~SparseMatrixPreconditionerWrapper() override; // Preconditioner interface - virtual void RightMultiply(const double* x, double* y) const; - virtual int num_rows() const; + void RightMultiply(const double* x, double* y) const override; + int num_rows() const override; private: - virtual bool UpdateImpl(const SparseMatrix& A, const double* D); + bool UpdateImpl(const SparseMatrix& A, const double* D) override; const SparseMatrix* matrix_; }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_PRECONDITIONER_H_ diff --git a/extern/ceres/internal/ceres/preprocessor.cc b/extern/ceres/internal/ceres/preprocessor.cc index 6a67d385645..44f0974dc5a 100644 --- a/extern/ceres/internal/ceres/preprocessor.cc +++ b/extern/ceres/internal/ceres/preprocessor.cc @@ -30,6 +30,8 @@ #include "ceres/preprocessor.h" +#include <memory> + #include "ceres/callbacks.h" #include "ceres/gradient_checking_cost_function.h" #include "ceres/line_search_preprocessor.h" @@ -41,22 +43,26 @@ namespace ceres { namespace internal { -Preprocessor* Preprocessor::Create(MinimizerType minimizer_type) { +std::unique_ptr<Preprocessor> Preprocessor::Create( + MinimizerType minimizer_type) { if (minimizer_type == TRUST_REGION) { - return new TrustRegionPreprocessor; + return std::make_unique<TrustRegionPreprocessor>(); } if (minimizer_type == LINE_SEARCH) { - return new LineSearchPreprocessor; + return std::make_unique<LineSearchPreprocessor>(); } LOG(FATAL) << "Unknown minimizer_type: " << minimizer_type; - return NULL; + return nullptr; } -Preprocessor::~Preprocessor() {} +Preprocessor::~Preprocessor() = default; void ChangeNumThreadsIfNeeded(Solver::Options* options) { + if (options->num_threads == 1) { + return; + } const int num_threads_available = MaxNumThreadsAvailable(); if (options->num_threads > num_threads_available) { LOG(WARNING) << "Specified options.num_threads: " << options->num_threads @@ -82,15 +88,15 @@ void SetupCommonMinimizerOptions(PreprocessedProblem* pp) { minimizer_options.evaluator = pp->evaluator; if (options.logging_type != SILENT) { - pp->logging_callback.reset(new LoggingCallback( - options.minimizer_type, options.minimizer_progress_to_stdout)); + pp->logging_callback = std::make_unique<LoggingCallback>( + options.minimizer_type, options.minimizer_progress_to_stdout); minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(), pp->logging_callback.get()); } if (options.update_state_every_iteration) { - pp->state_updating_callback.reset( - new StateUpdatingCallback(program, reduced_parameters)); + pp->state_updating_callback = + std::make_unique<StateUpdatingCallback>(program, reduced_parameters); // This must get pushed to the front of the callbacks so that it // is run before any of the user callbacks. minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(), diff --git a/extern/ceres/internal/ceres/preprocessor.h b/extern/ceres/internal/ceres/preprocessor.h index ec56c6e430a..b5db80af7e6 100644 --- a/extern/ceres/internal/ceres/preprocessor.h +++ b/extern/ceres/internal/ceres/preprocessor.h @@ -37,8 +37,9 @@ #include "ceres/coordinate_descent_minimizer.h" #include "ceres/evaluator.h" +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/iteration_callback.h" #include "ceres/linear_solver.h" #include "ceres/minimizer.h" @@ -67,10 +68,10 @@ struct PreprocessedProblem; // // The output of the Preprocessor is stored in a PreprocessedProblem // object. -class CERES_EXPORT_INTERNAL Preprocessor { +class CERES_NO_EXPORT Preprocessor { public: // Factory. - static Preprocessor* Create(MinimizerType minimizer_type); + static std::unique_ptr<Preprocessor> Create(MinimizerType minimizer_type); virtual ~Preprocessor(); virtual bool Preprocess(const Solver::Options& options, ProblemImpl* problem, @@ -79,8 +80,8 @@ class CERES_EXPORT_INTERNAL Preprocessor { // A PreprocessedProblem is the result of running the Preprocessor on // a Problem and Solver::Options object. -struct PreprocessedProblem { - PreprocessedProblem() : fixed_cost(0.0) {} +struct CERES_NO_EXPORT PreprocessedProblem { + PreprocessedProblem() = default; std::string error; Solver::Options options; @@ -100,7 +101,7 @@ struct PreprocessedProblem { std::vector<double*> removed_parameter_blocks; Vector reduced_parameters; - double fixed_cost; + double fixed_cost{0.0}; }; // Common functions used by various preprocessors. @@ -108,14 +109,18 @@ struct PreprocessedProblem { // If the user has specified a num_threads > the maximum number of threads // available from the compiled threading model, bound the number of threads // to the maximum. +CERES_NO_EXPORT void ChangeNumThreadsIfNeeded(Solver::Options* options); // Extract the effective parameter vector from the preprocessed // problem and setup bits of the Minimizer::Options object that are // common to all Preprocessors. +CERES_NO_EXPORT void SetupCommonMinimizerOptions(PreprocessedProblem* pp); } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_PREPROCESSOR_H_ diff --git a/extern/ceres/internal/ceres/problem.cc b/extern/ceres/internal/ceres/problem.cc index f3ffd546ef7..4269ca3ebc3 100644 --- a/extern/ceres/internal/ceres/problem.cc +++ b/extern/ceres/internal/ceres/problem.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2021 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -31,6 +31,7 @@ #include "ceres/problem.h" +#include <memory> #include <vector> #include "ceres/crs_matrix.h" @@ -46,7 +47,7 @@ Problem::Problem(const Problem::Options& options) // Not inline defaulted in declaration due to use of std::unique_ptr. Problem::Problem(Problem&&) = default; Problem& Problem::operator=(Problem&&) = default; -Problem::~Problem() {} +Problem::~Problem() = default; ResidualBlockId Problem::AddResidualBlock( CostFunction* cost_function, @@ -76,6 +77,10 @@ void Problem::AddParameterBlock(double* values, impl_->AddParameterBlock(values, size, local_parameterization); } +void Problem::AddParameterBlock(double* values, int size, Manifold* manifold) { + impl_->AddParameterBlock(values, size, manifold); +} + void Problem::RemoveResidualBlock(ResidualBlockId residual_block) { impl_->RemoveResidualBlock(residual_block); } @@ -106,6 +111,22 @@ const LocalParameterization* Problem::GetParameterization( return impl_->GetParameterization(values); } +bool Problem::HasParameterization(const double* values) const { + return impl_->HasParameterization(values); +} + +void Problem::SetManifold(double* values, Manifold* manifold) { + impl_->SetManifold(values, manifold); +} + +const Manifold* Problem::GetManifold(const double* values) const { + return impl_->GetManifold(values); +} + +bool Problem::HasManifold(const double* values) const { + return impl_->HasManifold(values); +} + void Problem::SetParameterLowerBound(double* values, int index, double lower_bound) { @@ -169,12 +190,16 @@ int Problem::NumResidualBlocks() const { return impl_->NumResidualBlocks(); } int Problem::NumResiduals() const { return impl_->NumResiduals(); } -int Problem::ParameterBlockSize(const double* parameter_block) const { - return impl_->ParameterBlockSize(parameter_block); +int Problem::ParameterBlockSize(const double* values) const { + return impl_->ParameterBlockSize(values); +} + +int Problem::ParameterBlockLocalSize(const double* values) const { + return impl_->ParameterBlockTangentSize(values); } -int Problem::ParameterBlockLocalSize(const double* parameter_block) const { - return impl_->ParameterBlockLocalSize(parameter_block); +int Problem::ParameterBlockTangentSize(const double* values) const { + return impl_->ParameterBlockTangentSize(values); } bool Problem::HasParameterBlock(const double* values) const { diff --git a/extern/ceres/internal/ceres/problem_impl.cc b/extern/ceres/internal/ceres/problem_impl.cc index 3155bc3569e..01a22c128be 100644 --- a/extern/ceres/internal/ceres/problem_impl.cc +++ b/extern/ceres/internal/ceres/problem_impl.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2019 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -49,9 +49,11 @@ #include "ceres/crs_matrix.h" #include "ceres/evaluation_callback.h" #include "ceres/evaluator.h" +#include "ceres/internal/export.h" #include "ceres/internal/fixed_array.h" -#include "ceres/internal/port.h" #include "ceres/loss_function.h" +#include "ceres/manifold.h" +#include "ceres/manifold_adapter.h" #include "ceres/map_util.h" #include "ceres/parameter_block.h" #include "ceres/program.h" @@ -64,11 +66,6 @@ namespace ceres { namespace internal { - -using std::map; -using std::string; -using std::vector; - namespace { // Returns true if two regions of memory, a and b, with sizes size_a and size_b // respectively, overlap. @@ -130,7 +127,7 @@ ParameterBlock* ProblemImpl::InternalAddParameterBlock(double* values, << "for a parameter with size " << size; // Ignore the request if there is a block for the given pointer already. - ParameterMap::iterator it = parameter_block_map_.find(values); + auto it = parameter_block_map_.find(values); if (it != parameter_block_map_.end()) { if (!options_.disable_all_safety_checks) { int existing_size = it->second->Size(); @@ -146,11 +143,11 @@ ParameterBlock* ProblemImpl::InternalAddParameterBlock(double* values, // Before adding the parameter block, also check that it doesn't alias any // other parameter blocks. if (!parameter_block_map_.empty()) { - ParameterMap::iterator lb = parameter_block_map_.lower_bound(values); + auto lb = parameter_block_map_.lower_bound(values); // If lb is not the first block, check the previous block for aliasing. if (lb != parameter_block_map_.begin()) { - ParameterMap::iterator previous = lb; + auto previous = lb; --previous; CheckForNoAliasing( previous->first, previous->second->Size(), values, size); @@ -165,7 +162,7 @@ ParameterBlock* ProblemImpl::InternalAddParameterBlock(double* values, // Pass the index of the new parameter block as well to keep the index in // sync with the position of the parameter in the program's parameter vector. - ParameterBlock* new_parameter_block = + auto* new_parameter_block = new ParameterBlock(values, size, program_->parameter_blocks_.size()); // For dynamic problems, add the list of dependent residual blocks, which is @@ -192,7 +189,7 @@ void ProblemImpl::InternalRemoveResidualBlock(ResidualBlock* residual_block) { residual_block); } - ResidualBlockSet::iterator it = residual_block_set_.find(residual_block); + auto it = residual_block_set_.find(residual_block); residual_block_set_.erase(it); } DeleteBlockInVector(program_->mutable_residual_blocks(), residual_block); @@ -207,13 +204,13 @@ void ProblemImpl::DeleteBlock(ResidualBlock* residual_block) { // The const casts here are legit, since ResidualBlock holds these // pointers as const pointers but we have ownership of them and // have the right to destroy them when the destructor is called. - CostFunction* cost_function = + auto* cost_function = const_cast<CostFunction*>(residual_block->cost_function()); if (options_.cost_function_ownership == TAKE_OWNERSHIP) { DecrementValueOrDeleteKey(cost_function, &cost_function_ref_count_); } - LossFunction* loss_function = + auto* loss_function = const_cast<LossFunction*>(residual_block->loss_function()); if (options_.loss_function_ownership == TAKE_OWNERSHIP && loss_function != nullptr) { @@ -225,15 +222,7 @@ void ProblemImpl::DeleteBlock(ResidualBlock* residual_block) { // Deletes the parameter block in question, assuming there are no other // references to it inside the problem (e.g. by any residual blocks). -// Referenced parameterizations are tucked away for future deletion, since it -// is not possible to know whether other parts of the problem depend on them -// without doing a full scan. void ProblemImpl::DeleteBlock(ParameterBlock* parameter_block) { - if (options_.local_parameterization_ownership == TAKE_OWNERSHIP && - parameter_block->local_parameterization() != nullptr) { - local_parameterizations_to_delete_.push_back( - parameter_block->mutable_local_parameterization()); - } parameter_block_map_.erase(parameter_block->mutable_user_state()); delete parameter_block; } @@ -264,14 +253,18 @@ ProblemImpl::~ProblemImpl() { } // Collect the unique parameterizations and delete the parameters. - for (int i = 0; i < program_->parameter_blocks_.size(); ++i) { - DeleteBlock(program_->parameter_blocks_[i]); + for (auto* parameter_block : program_->parameter_blocks_) { + DeleteBlock(parameter_block); } // Delete the owned parameterizations. STLDeleteUniqueContainerPointers(local_parameterizations_to_delete_.begin(), local_parameterizations_to_delete_.end()); + // Delete the owned manifolds. + STLDeleteUniqueContainerPointers(manifolds_to_delete_.begin(), + manifolds_to_delete_.end()); + if (context_impl_owned_) { delete context_impl_; } @@ -286,7 +279,7 @@ ResidualBlockId ProblemImpl::AddResidualBlock( CHECK_EQ(num_parameter_blocks, cost_function->parameter_block_sizes().size()); // Check the sizes match. - const vector<int32_t>& parameter_block_sizes = + const std::vector<int32_t>& parameter_block_sizes = cost_function->parameter_block_sizes(); if (!options_.disable_all_safety_checks) { @@ -295,7 +288,7 @@ ResidualBlockId ProblemImpl::AddResidualBlock( << "that the cost function expects."; // Check for duplicate parameter blocks. - vector<double*> sorted_parameter_blocks( + std::vector<double*> sorted_parameter_blocks( parameter_blocks, parameter_blocks + num_parameter_blocks); sort(sorted_parameter_blocks.begin(), sorted_parameter_blocks.end()); const bool has_duplicate_items = @@ -303,7 +296,7 @@ ResidualBlockId ProblemImpl::AddResidualBlock( sorted_parameter_blocks.end()) != sorted_parameter_blocks.end()); if (has_duplicate_items) { - string blocks; + std::string blocks; for (int i = 0; i < num_parameter_blocks; ++i) { blocks += StringPrintf(" %p ", parameter_blocks[i]); } @@ -315,7 +308,7 @@ ResidualBlockId ProblemImpl::AddResidualBlock( } // Add parameter blocks and convert the double*'s to parameter blocks. - vector<ParameterBlock*> parameter_block_ptrs(num_parameter_blocks); + std::vector<ParameterBlock*> parameter_block_ptrs(num_parameter_blocks); for (int i = 0; i < num_parameter_blocks; ++i) { parameter_block_ptrs[i] = InternalAddParameterBlock( parameter_blocks[i], parameter_block_sizes[i]); @@ -334,7 +327,7 @@ ResidualBlockId ProblemImpl::AddResidualBlock( } } - ResidualBlock* new_residual_block = + auto* new_residual_block = new ResidualBlock(cost_function, loss_function, parameter_block_ptrs, @@ -372,12 +365,50 @@ void ProblemImpl::AddParameterBlock(double* values, int size) { InternalAddParameterBlock(values, size); } +void ProblemImpl::InternalSetParameterization( + double* values, + ParameterBlock* parameter_block, + LocalParameterization* local_parameterization) { + parameter_block_to_local_param_[values] = local_parameterization; + Manifold* manifold = nullptr; + if (local_parameterization != nullptr) { + if (options_.local_parameterization_ownership == TAKE_OWNERSHIP) { + local_parameterizations_to_delete_.push_back(local_parameterization); + } + + manifold = new ManifoldAdapter(local_parameterization); + // Add the manifold to manifolds_to_delete_ unconditionally since + // we own it and it will need to be deleted. + manifolds_to_delete_.push_back(manifold); + } + + parameter_block->SetManifold(manifold); +} + +void ProblemImpl::InternalSetManifold(double* values, + ParameterBlock* parameter_block, + Manifold* manifold) { + // Reset any association between this parameter block and a local + // parameterization. This only needs done while we are in the transition from + // LocalParameterization to Manifold. + parameter_block_to_local_param_[values] = nullptr; + if (manifold != nullptr && options_.manifold_ownership == TAKE_OWNERSHIP) { + manifolds_to_delete_.push_back(manifold); + } + parameter_block->SetManifold(manifold); +} + void ProblemImpl::AddParameterBlock( double* values, int size, LocalParameterization* local_parameterization) { ParameterBlock* parameter_block = InternalAddParameterBlock(values, size); - if (local_parameterization != nullptr) { - parameter_block->SetParameterization(local_parameterization); - } + InternalSetParameterization(values, parameter_block, local_parameterization); +} + +void ProblemImpl::AddParameterBlock(double* values, + int size, + Manifold* manifold) { + ParameterBlock* parameter_block = InternalAddParameterBlock(values, size); + InternalSetManifold(values, parameter_block, manifold); } // Delete a block from a vector of blocks, maintaining the indexing invariant. @@ -385,7 +416,7 @@ void ProblemImpl::AddParameterBlock( // vector over the element to remove, then popping the last element. It // destroys the ordering in the interest of speed. template <typename Block> -void ProblemImpl::DeleteBlockInVector(vector<Block*>* mutable_blocks, +void ProblemImpl::DeleteBlockInVector(std::vector<Block*>* mutable_blocks, Block* block_to_remove) { CHECK_EQ((*mutable_blocks)[block_to_remove->index()], block_to_remove) << "You found a Ceres bug! \n" @@ -411,7 +442,7 @@ void ProblemImpl::RemoveResidualBlock(ResidualBlock* residual_block) { CHECK(residual_block != nullptr); // Verify that residual_block identifies a residual in the current problem. - const string residual_not_found_message = StringPrintf( + const std::string residual_not_found_message = StringPrintf( "Residual block to remove: %p not found. This usually means " "one of three things have happened:\n" " 1) residual_block is uninitialised and points to a random " @@ -449,11 +480,11 @@ void ProblemImpl::RemoveParameterBlock(const double* values) { if (options_.enable_fast_removal) { // Copy the dependent residuals from the parameter block because the set of // dependents will change after each call to RemoveResidualBlock(). - vector<ResidualBlock*> residual_blocks_to_remove( + std::vector<ResidualBlock*> residual_blocks_to_remove( parameter_block->mutable_residual_blocks()->begin(), parameter_block->mutable_residual_blocks()->end()); - for (int i = 0; i < residual_blocks_to_remove.size(); ++i) { - InternalRemoveResidualBlock(residual_blocks_to_remove[i]); + for (auto* residual_block : residual_blocks_to_remove) { + InternalRemoveResidualBlock(residual_block); } } else { // Scan all the residual blocks to remove ones that depend on the parameter @@ -518,20 +549,31 @@ void ProblemImpl::SetParameterization( << "you can set its local parameterization."; } - // If the parameter block already has a local parameterization and - // we are to take ownership of local parameterizations, then add it - // to local_parameterizations_to_delete_ for eventual deletion. - if (parameter_block->local_parameterization_ && - options_.local_parameterization_ownership == TAKE_OWNERSHIP) { - local_parameterizations_to_delete_.push_back( - parameter_block->local_parameterization_); + InternalSetParameterization(values, parameter_block, local_parameterization); +} + +void ProblemImpl::SetManifold(double* values, Manifold* manifold) { + ParameterBlock* parameter_block = + FindWithDefault(parameter_block_map_, values, nullptr); + if (parameter_block == nullptr) { + LOG(FATAL) << "Parameter block not found: " << values + << ". You must add the parameter block to the problem before " + << "you can set its manifold."; } - parameter_block->SetParameterization(local_parameterization); + InternalSetManifold(values, parameter_block, manifold); } const LocalParameterization* ProblemImpl::GetParameterization( const double* values) const { + return FindWithDefault(parameter_block_to_local_param_, values, nullptr); +} + +bool ProblemImpl::HasParameterization(const double* values) const { + return GetParameterization(values) != nullptr; +} + +const Manifold* ProblemImpl::GetManifold(const double* values) const { ParameterBlock* parameter_block = FindWithDefault( parameter_block_map_, const_cast<double*>(values), nullptr); if (parameter_block == nullptr) { @@ -540,7 +582,11 @@ const LocalParameterization* ProblemImpl::GetParameterization( << "you can get its local parameterization."; } - return parameter_block->local_parameterization(); + return parameter_block->manifold(); +} + +bool ProblemImpl::HasManifold(const double* values) const { + return GetManifold(values) != nullptr; } void ProblemImpl::SetParameterLowerBound(double* values, @@ -596,8 +642,8 @@ double ProblemImpl::GetParameterUpperBound(const double* values, bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options, double* cost, - vector<double>* residuals, - vector<double>* gradient, + std::vector<double>* residuals, + std::vector<double>* gradient, CRSMatrix* jacobian) { if (cost == nullptr && residuals == nullptr && gradient == nullptr && jacobian == nullptr) { @@ -612,11 +658,11 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options, ? evaluate_options.residual_blocks : program_->residual_blocks()); - const vector<double*>& parameter_block_ptrs = + const std::vector<double*>& parameter_block_ptrs = evaluate_options.parameter_blocks; - vector<ParameterBlock*> variable_parameter_blocks; - vector<ParameterBlock*>& parameter_blocks = + std::vector<ParameterBlock*> variable_parameter_blocks; + std::vector<ParameterBlock*>& parameter_blocks = *program.mutable_parameter_blocks(); if (parameter_block_ptrs.size() == 0) { @@ -649,11 +695,12 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options, // columns of the jacobian, we need to make sure that they are // constant during evaluation and then make them variable again // after we are done. - vector<ParameterBlock*> all_parameter_blocks(program_->parameter_blocks()); - vector<ParameterBlock*> included_parameter_blocks( + std::vector<ParameterBlock*> all_parameter_blocks( + program_->parameter_blocks()); + std::vector<ParameterBlock*> included_parameter_blocks( program.parameter_blocks()); - vector<ParameterBlock*> excluded_parameter_blocks; + std::vector<ParameterBlock*> excluded_parameter_blocks; sort(all_parameter_blocks.begin(), all_parameter_blocks.end()); sort(included_parameter_blocks.begin(), included_parameter_blocks.end()); set_difference(all_parameter_blocks.begin(), @@ -663,8 +710,7 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options, back_inserter(excluded_parameter_blocks)); variable_parameter_blocks.reserve(excluded_parameter_blocks.size()); - for (int i = 0; i < excluded_parameter_blocks.size(); ++i) { - ParameterBlock* parameter_block = excluded_parameter_blocks[i]; + for (auto* parameter_block : excluded_parameter_blocks) { if (!parameter_block->IsConstant()) { variable_parameter_blocks.push_back(parameter_block); parameter_block->SetConstant(); @@ -716,8 +762,8 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options, std::unique_ptr<CompressedRowSparseMatrix> tmp_jacobian; if (jacobian != nullptr) { - tmp_jacobian.reset( - down_cast<CompressedRowSparseMatrix*>(evaluator->CreateJacobian())); + tmp_jacobian.reset(down_cast<CompressedRowSparseMatrix*>( + evaluator->CreateJacobian().release())); } // Point the state pointers to the user state pointers. This is @@ -749,8 +795,8 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options, // Make the parameter blocks that were temporarily marked constant, // variable again. - for (int i = 0; i < variable_parameter_blocks.size(); ++i) { - variable_parameter_blocks[i]->SetVarying(); + for (auto* parameter_block : variable_parameter_blocks) { + parameter_block->SetVarying(); } if (status) { @@ -829,24 +875,25 @@ int ProblemImpl::ParameterBlockSize(const double* values) const { return parameter_block->Size(); } -int ProblemImpl::ParameterBlockLocalSize(const double* values) const { +int ProblemImpl::ParameterBlockTangentSize(const double* values) const { ParameterBlock* parameter_block = FindWithDefault( parameter_block_map_, const_cast<double*>(values), nullptr); if (parameter_block == nullptr) { LOG(FATAL) << "Parameter block not found: " << values << ". You must add the parameter block to the problem before " - << "you can get its local size."; + << "you can get its tangent size."; } - return parameter_block->LocalSize(); + return parameter_block->TangentSize(); } -bool ProblemImpl::HasParameterBlock(const double* parameter_block) const { - return (parameter_block_map_.find(const_cast<double*>(parameter_block)) != +bool ProblemImpl::HasParameterBlock(const double* values) const { + return (parameter_block_map_.find(const_cast<double*>(values)) != parameter_block_map_.end()); } -void ProblemImpl::GetParameterBlocks(vector<double*>* parameter_blocks) const { +void ProblemImpl::GetParameterBlocks( + std::vector<double*>* parameter_blocks) const { CHECK(parameter_blocks != nullptr); parameter_blocks->resize(0); parameter_blocks->reserve(parameter_block_map_.size()); @@ -856,14 +903,14 @@ void ProblemImpl::GetParameterBlocks(vector<double*>* parameter_blocks) const { } void ProblemImpl::GetResidualBlocks( - vector<ResidualBlockId>* residual_blocks) const { + std::vector<ResidualBlockId>* residual_blocks) const { CHECK(residual_blocks != nullptr); *residual_blocks = program().residual_blocks(); } void ProblemImpl::GetParameterBlocksForResidualBlock( const ResidualBlockId residual_block, - vector<double*>* parameter_blocks) const { + std::vector<double*>* parameter_blocks) const { int num_parameter_blocks = residual_block->NumParameterBlocks(); CHECK(parameter_blocks != nullptr); parameter_blocks->resize(num_parameter_blocks); @@ -884,7 +931,7 @@ const LossFunction* ProblemImpl::GetLossFunctionForResidualBlock( } void ProblemImpl::GetResidualBlocksForParameterBlock( - const double* values, vector<ResidualBlockId>* residual_blocks) const { + const double* values, std::vector<ResidualBlockId>* residual_blocks) const { ParameterBlock* parameter_block = FindWithDefault( parameter_block_map_, const_cast<double*>(values), nullptr); if (parameter_block == nullptr) { diff --git a/extern/ceres/internal/ceres/problem_impl.h b/extern/ceres/internal/ceres/problem_impl.h index 9abff3f19ae..22073b674f1 100644 --- a/extern/ceres/internal/ceres/problem_impl.h +++ b/extern/ceres/internal/ceres/problem_impl.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2019 Google Inc. All rights reserved. +// Copyright 2021 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -42,11 +42,15 @@ #include <array> #include <map> #include <memory> +#include <unordered_map> #include <unordered_set> #include <vector> #include "ceres/context_impl.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/internal/port.h" +#include "ceres/manifold.h" #include "ceres/problem.h" #include "ceres/types.h" @@ -63,12 +67,12 @@ namespace internal { class Program; class ResidualBlock; -class CERES_EXPORT_INTERNAL ProblemImpl { +class CERES_NO_EXPORT ProblemImpl { public: - typedef std::map<double*, ParameterBlock*> ParameterMap; - typedef std::unordered_set<ResidualBlock*> ResidualBlockSet; - typedef std::map<CostFunction*, int> CostFunctionRefCount; - typedef std::map<LossFunction*, int> LossFunctionRefCount; + using ParameterMap = std::map<double*, ParameterBlock*>; + using ResidualBlockSet = std::unordered_set<ResidualBlock*>; + using CostFunctionRefCount = std::map<CostFunction*, int>; + using LossFunctionRefCount = std::map<LossFunction*, int>; ProblemImpl(); explicit ProblemImpl(const Problem::Options& options); @@ -100,6 +104,8 @@ class CERES_EXPORT_INTERNAL ProblemImpl { int size, LocalParameterization* local_parameterization); + void AddParameterBlock(double* values, int size, Manifold* manifold); + void RemoveResidualBlock(ResidualBlock* residual_block); void RemoveParameterBlock(const double* values); @@ -110,6 +116,11 @@ class CERES_EXPORT_INTERNAL ProblemImpl { void SetParameterization(double* values, LocalParameterization* local_parameterization); const LocalParameterization* GetParameterization(const double* values) const; + bool HasParameterization(const double* values) const; + + void SetManifold(double* values, Manifold* manifold); + const Manifold* GetManifold(const double* values) const; + bool HasManifold(const double* values) const; void SetParameterLowerBound(double* values, int index, double lower_bound); void SetParameterUpperBound(double* values, int index, double upper_bound); @@ -134,10 +145,10 @@ class CERES_EXPORT_INTERNAL ProblemImpl { int NumResidualBlocks() const; int NumResiduals() const; - int ParameterBlockSize(const double* parameter_block) const; - int ParameterBlockLocalSize(const double* parameter_block) const; + int ParameterBlockSize(const double* values) const; + int ParameterBlockTangentSize(const double* values) const; - bool HasParameterBlock(const double* parameter_block) const; + bool HasParameterBlock(const double* values) const; void GetParameterBlocks(std::vector<double*>* parameter_blocks) const; void GetResidualBlocks(std::vector<ResidualBlockId>* residual_blocks) const; @@ -169,6 +180,14 @@ class CERES_EXPORT_INTERNAL ProblemImpl { private: ParameterBlock* InternalAddParameterBlock(double* values, int size); + void InternalSetParameterization( + double* values, + ParameterBlock* parameter_block, + LocalParameterization* local_parameterization); + void InternalSetManifold(double* values, + ParameterBlock* parameter_block, + Manifold* manifold); + void InternalRemoveResidualBlock(ResidualBlock* residual_block); // Delete the arguments in question. These differ from the Remove* functions @@ -194,23 +213,45 @@ class CERES_EXPORT_INTERNAL ProblemImpl { // The actual parameter and residual blocks. std::unique_ptr<internal::Program> program_; + // TODO(sameeragarwal): Unify the shared object handling across object types. + // Right now we are using vectors for LocalParameterization and Manifold + // objects and reference counting for CostFunctions and LossFunctions. Ideally + // this should be done uniformly. + // When removing parameter blocks, parameterizations have ambiguous // ownership. Instead of scanning the entire problem to see if the // parameterization is shared with other parameter blocks, buffer // them until destruction. - // - // TODO(keir): See if it makes sense to use sets instead. std::vector<LocalParameterization*> local_parameterizations_to_delete_; + // When removing parameter blocks, manifolds have ambiguous + // ownership. Instead of scanning the entire problem to see if the + // manifold is shared with other parameter blocks, buffer + // them until destruction. + std::vector<Manifold*> manifolds_to_delete_; + // For each cost function and loss function in the problem, a count // of the number of residual blocks that refer to them. When the // count goes to zero and the problem owns these objects, they are // destroyed. CostFunctionRefCount cost_function_ref_count_; LossFunctionRefCount loss_function_ref_count_; + + // Because we wrap LocalParameterization objects using a ManifoldAdapter, when + // the user calls GetParameterization we cannot use the same logic as + // GetManifold as the ParameterBlock object only returns a Manifold object. So + // this map stores the association between parameter blocks and local + // parameterizations. + // + // This is a temporary object which will be removed once the + // LocalParameterization to Manifold transition is complete. + std::unordered_map<const double*, LocalParameterization*> + parameter_block_to_local_param_; }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_PUBLIC_PROBLEM_IMPL_H_ diff --git a/extern/ceres/internal/ceres/program.cc b/extern/ceres/internal/ceres/program.cc index f1ded2e5d5a..1cb9ebcbe73 100644 --- a/extern/ceres/internal/ceres/program.cc +++ b/extern/ceres/internal/ceres/program.cc @@ -33,6 +33,7 @@ #include <algorithm> #include <map> #include <memory> +#include <string> #include <vector> #include "ceres/array_utils.h" @@ -40,9 +41,9 @@ #include "ceres/compressed_row_sparse_matrix.h" #include "ceres/cost_function.h" #include "ceres/evaluator.h" -#include "ceres/internal/port.h" -#include "ceres/local_parameterization.h" +#include "ceres/internal/export.h" #include "ceres/loss_function.h" +#include "ceres/manifold.h" #include "ceres/map_util.h" #include "ceres/parameter_block.h" #include "ceres/problem.h" @@ -53,31 +54,19 @@ namespace ceres { namespace internal { -using std::max; -using std::set; -using std::string; -using std::vector; - -Program::Program() {} - -Program::Program(const Program& program) - : parameter_blocks_(program.parameter_blocks_), - residual_blocks_(program.residual_blocks_), - evaluation_callback_(program.evaluation_callback_) {} - -const vector<ParameterBlock*>& Program::parameter_blocks() const { +const std::vector<ParameterBlock*>& Program::parameter_blocks() const { return parameter_blocks_; } -const vector<ResidualBlock*>& Program::residual_blocks() const { +const std::vector<ResidualBlock*>& Program::residual_blocks() const { return residual_blocks_; } -vector<ParameterBlock*>* Program::mutable_parameter_blocks() { +std::vector<ParameterBlock*>* Program::mutable_parameter_blocks() { return ¶meter_blocks_; } -vector<ResidualBlock*>* Program::mutable_residual_blocks() { +std::vector<ResidualBlock*>* Program::mutable_residual_blocks() { return &residual_blocks_; } @@ -86,33 +75,32 @@ EvaluationCallback* Program::mutable_evaluation_callback() { } bool Program::StateVectorToParameterBlocks(const double* state) { - for (int i = 0; i < parameter_blocks_.size(); ++i) { - if (!parameter_blocks_[i]->IsConstant() && - !parameter_blocks_[i]->SetState(state)) { + for (auto* parameter_block : parameter_blocks_) { + if (!parameter_block->IsConstant() && !parameter_block->SetState(state)) { return false; } - state += parameter_blocks_[i]->Size(); + state += parameter_block->Size(); } return true; } void Program::ParameterBlocksToStateVector(double* state) const { - for (int i = 0; i < parameter_blocks_.size(); ++i) { - parameter_blocks_[i]->GetState(state); - state += parameter_blocks_[i]->Size(); + for (auto* parameter_block : parameter_blocks_) { + parameter_block->GetState(state); + state += parameter_block->Size(); } } void Program::CopyParameterBlockStateToUserState() { - for (int i = 0; i < parameter_blocks_.size(); ++i) { - parameter_blocks_[i]->GetState(parameter_blocks_[i]->mutable_user_state()); + for (auto* parameter_block : parameter_blocks_) { + parameter_block->GetState(parameter_block->mutable_user_state()); } } bool Program::SetParameterBlockStatePtrsToUserStatePtrs() { - for (int i = 0; i < parameter_blocks_.size(); ++i) { - if (!parameter_blocks_[i]->IsConstant() && - !parameter_blocks_[i]->SetState(parameter_blocks_[i]->user_state())) { + for (auto* parameter_block : parameter_blocks_) { + if (!parameter_block->IsConstant() && + !parameter_block->SetState(parameter_block->user_state())) { return false; } } @@ -122,13 +110,13 @@ bool Program::SetParameterBlockStatePtrsToUserStatePtrs() { bool Program::Plus(const double* state, const double* delta, double* state_plus_delta) const { - for (int i = 0; i < parameter_blocks_.size(); ++i) { - if (!parameter_blocks_[i]->Plus(state, delta, state_plus_delta)) { + for (auto* parameter_block : parameter_blocks_) { + if (!parameter_block->Plus(state, delta, state_plus_delta)) { return false; } - state += parameter_blocks_[i]->Size(); - delta += parameter_blocks_[i]->LocalSize(); - state_plus_delta += parameter_blocks_[i]->Size(); + state += parameter_block->Size(); + delta += parameter_block->TangentSize(); + state_plus_delta += parameter_block->Size(); } return true; } @@ -136,8 +124,7 @@ bool Program::Plus(const double* state, void Program::SetParameterOffsetsAndIndex() { // Set positions for all parameters appearing as arguments to residuals to one // past the end of the parameter block array. - for (int i = 0; i < residual_blocks_.size(); ++i) { - ResidualBlock* residual_block = residual_blocks_[i]; + for (auto* residual_block : residual_blocks_) { for (int j = 0; j < residual_block->NumParameterBlocks(); ++j) { residual_block->parameter_blocks()[j]->set_index(-1); } @@ -150,7 +137,7 @@ void Program::SetParameterOffsetsAndIndex() { parameter_blocks_[i]->set_state_offset(state_offset); parameter_blocks_[i]->set_delta_offset(delta_offset); state_offset += parameter_blocks_[i]->Size(); - delta_offset += parameter_blocks_[i]->LocalSize(); + delta_offset += parameter_blocks_[i]->TangentSize(); } } @@ -178,16 +165,15 @@ bool Program::IsValid() const { } state_offset += parameter_blocks_[i]->Size(); - delta_offset += parameter_blocks_[i]->LocalSize(); + delta_offset += parameter_blocks_[i]->TangentSize(); } return true; } -bool Program::ParameterBlocksAreFinite(string* message) const { +bool Program::ParameterBlocksAreFinite(std::string* message) const { CHECK(message != nullptr); - for (int i = 0; i < parameter_blocks_.size(); ++i) { - const ParameterBlock* parameter_block = parameter_blocks_[i]; + for (auto* parameter_block : parameter_blocks_) { const double* array = parameter_block->user_state(); const int size = parameter_block->Size(); const int invalid_index = FindInvalidValue(size, array); @@ -207,8 +193,7 @@ bool Program::ParameterBlocksAreFinite(string* message) const { } bool Program::IsBoundsConstrained() const { - for (int i = 0; i < parameter_blocks_.size(); ++i) { - const ParameterBlock* parameter_block = parameter_blocks_[i]; + for (auto* parameter_block : parameter_blocks_) { if (parameter_block->IsConstant()) { continue; } @@ -225,10 +210,9 @@ bool Program::IsBoundsConstrained() const { return false; } -bool Program::IsFeasible(string* message) const { +bool Program::IsFeasible(std::string* message) const { CHECK(message != nullptr); - for (int i = 0; i < parameter_blocks_.size(); ++i) { - const ParameterBlock* parameter_block = parameter_blocks_[i]; + for (auto* parameter_block : parameter_blocks_) { const double* parameters = parameter_block->user_state(); const int size = parameter_block->Size(); if (parameter_block->IsConstant()) { @@ -284,42 +268,42 @@ bool Program::IsFeasible(string* message) const { return true; } -Program* Program::CreateReducedProgram( - vector<double*>* removed_parameter_blocks, +std::unique_ptr<Program> Program::CreateReducedProgram( + std::vector<double*>* removed_parameter_blocks, double* fixed_cost, - string* error) const { + std::string* error) const { CHECK(removed_parameter_blocks != nullptr); CHECK(fixed_cost != nullptr); CHECK(error != nullptr); - std::unique_ptr<Program> reduced_program(new Program(*this)); + std::unique_ptr<Program> reduced_program = std::make_unique<Program>(*this); if (!reduced_program->RemoveFixedBlocks( removed_parameter_blocks, fixed_cost, error)) { return nullptr; } reduced_program->SetParameterOffsetsAndIndex(); - return reduced_program.release(); + return reduced_program; } -bool Program::RemoveFixedBlocks(vector<double*>* removed_parameter_blocks, +bool Program::RemoveFixedBlocks(std::vector<double*>* removed_parameter_blocks, double* fixed_cost, - string* error) { + std::string* error) { CHECK(removed_parameter_blocks != nullptr); CHECK(fixed_cost != nullptr); CHECK(error != nullptr); std::unique_ptr<double[]> residual_block_evaluate_scratch; - residual_block_evaluate_scratch.reset( - new double[MaxScratchDoublesNeededForEvaluate()]); + residual_block_evaluate_scratch = + std::make_unique<double[]>(MaxScratchDoublesNeededForEvaluate()); *fixed_cost = 0.0; bool need_to_call_prepare_for_evaluation = evaluation_callback_ != nullptr; // Mark all the parameters as unused. Abuse the index member of the // parameter blocks for the marking. - for (int i = 0; i < parameter_blocks_.size(); ++i) { - parameter_blocks_[i]->set_index(-1); + for (auto* parameter_block : parameter_blocks_) { + parameter_block->set_index(-1); } // Filter out residual that have all-constant parameters, and mark @@ -391,8 +375,7 @@ bool Program::RemoveFixedBlocks(vector<double*>* removed_parameter_blocks, // Filter out unused or fixed parameter blocks. int num_active_parameter_blocks = 0; removed_parameter_blocks->clear(); - for (int i = 0; i < parameter_blocks_.size(); ++i) { - ParameterBlock* parameter_block = parameter_blocks_[i]; + for (auto* parameter_block : parameter_blocks_) { if (parameter_block->index() == -1) { removed_parameter_blocks->push_back( parameter_block->mutable_user_state()); @@ -412,7 +395,7 @@ bool Program::RemoveFixedBlocks(vector<double*>* removed_parameter_blocks, } bool Program::IsParameterBlockSetIndependent( - const set<double*>& independent_set) const { + const std::set<double*>& independent_set) const { // Loop over each residual block and ensure that no two parameter // blocks in the same residual block are part of // parameter_block_ptrs as that would violate the assumption that it @@ -483,24 +466,24 @@ int Program::NumParameterBlocks() const { return parameter_blocks_.size(); } int Program::NumResiduals() const { int num_residuals = 0; - for (int i = 0; i < residual_blocks_.size(); ++i) { - num_residuals += residual_blocks_[i]->NumResiduals(); + for (auto* residual_block : residual_blocks_) { + num_residuals += residual_block->NumResiduals(); } return num_residuals; } int Program::NumParameters() const { int num_parameters = 0; - for (int i = 0; i < parameter_blocks_.size(); ++i) { - num_parameters += parameter_blocks_[i]->Size(); + for (auto* parameter_block : parameter_blocks_) { + num_parameters += parameter_block->Size(); } return num_parameters; } int Program::NumEffectiveParameters() const { int num_parameters = 0; - for (int i = 0; i < parameter_blocks_.size(); ++i) { - num_parameters += parameter_blocks_[i]->LocalSize(); + for (auto* parameter_block : parameter_blocks_) { + num_parameters += parameter_block->TangentSize(); } return num_parameters; } @@ -511,48 +494,47 @@ int Program::NumEffectiveParameters() const { int Program::MaxScratchDoublesNeededForEvaluate() const { // Compute the scratch space needed for evaluate. int max_scratch_bytes_for_evaluate = 0; - for (int i = 0; i < residual_blocks_.size(); ++i) { + for (auto* residual_block : residual_blocks_) { max_scratch_bytes_for_evaluate = - max(max_scratch_bytes_for_evaluate, - residual_blocks_[i]->NumScratchDoublesForEvaluate()); + std::max(max_scratch_bytes_for_evaluate, + residual_block->NumScratchDoublesForEvaluate()); } return max_scratch_bytes_for_evaluate; } int Program::MaxDerivativesPerResidualBlock() const { int max_derivatives = 0; - for (int i = 0; i < residual_blocks_.size(); ++i) { + for (auto* residual_block : residual_blocks_) { int derivatives = 0; - ResidualBlock* residual_block = residual_blocks_[i]; int num_parameters = residual_block->NumParameterBlocks(); for (int j = 0; j < num_parameters; ++j) { derivatives += residual_block->NumResiduals() * - residual_block->parameter_blocks()[j]->LocalSize(); + residual_block->parameter_blocks()[j]->TangentSize(); } - max_derivatives = max(max_derivatives, derivatives); + max_derivatives = std::max(max_derivatives, derivatives); } return max_derivatives; } int Program::MaxParametersPerResidualBlock() const { int max_parameters = 0; - for (int i = 0; i < residual_blocks_.size(); ++i) { + for (auto* residual_block : residual_blocks_) { max_parameters = - max(max_parameters, residual_blocks_[i]->NumParameterBlocks()); + std::max(max_parameters, residual_block->NumParameterBlocks()); } return max_parameters; } int Program::MaxResidualsPerResidualBlock() const { int max_residuals = 0; - for (int i = 0; i < residual_blocks_.size(); ++i) { - max_residuals = max(max_residuals, residual_blocks_[i]->NumResiduals()); + for (auto* residual_block : residual_blocks_) { + max_residuals = std::max(max_residuals, residual_block->NumResiduals()); } return max_residuals; } -string Program::ToString() const { - string ret = "Program dump\n"; +std::string Program::ToString() const { + std::string ret = "Program dump\n"; ret += StringPrintf("Number of parameter blocks: %d\n", NumParameterBlocks()); ret += StringPrintf("Number of parameters: %d\n", NumParameters()); ret += "Parameters:\n"; diff --git a/extern/ceres/internal/ceres/program.h b/extern/ceres/internal/ceres/program.h index ca29d316284..4dbd1ba5ff1 100644 --- a/extern/ceres/internal/ceres/program.h +++ b/extern/ceres/internal/ceres/program.h @@ -37,7 +37,8 @@ #include <vector> #include "ceres/evaluation_callback.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -57,11 +58,8 @@ class TripletSparseMatrix; // another; for example, the first stage of solving involves stripping all // constant parameters and residuals. This is in contrast with Problem, which is // not built for transformation. -class CERES_EXPORT_INTERNAL Program { +class CERES_NO_EXPORT Program { public: - Program(); - explicit Program(const Program& program); - // The ordered parameter and residual blocks for the program. const std::vector<ParameterBlock*>& parameter_blocks() const; const std::vector<ResidualBlock*>& residual_blocks() const; @@ -72,9 +70,9 @@ class CERES_EXPORT_INTERNAL Program { // Serialize to/from the program and update states. // // NOTE: Setting the state of a parameter block can trigger the - // computation of the Jacobian of its local parameterization. If - // this computation fails for some reason, then this method returns - // false and the state of the parameter blocks cannot be trusted. + // computation of the Jacobian of its manifold. If this computation fails for + // some reason, then this method returns false and the state of the parameter + // blocks cannot be trusted. bool StateVectorToParameterBlocks(const double* state); void ParameterBlocksToStateVector(double* state) const; @@ -82,8 +80,8 @@ class CERES_EXPORT_INTERNAL Program { void CopyParameterBlockStateToUserState(); // Set the parameter block pointers to the user pointers. Since this - // runs parameter block set state internally, which may call local - // parameterizations, this can fail. False is returned on failure. + // runs parameter block set state internally, which may call manifold, this + // can fail. False is returned on failure. bool SetParameterBlockStatePtrsToUserStatePtrs(); // Update a state vector for the program given a delta. @@ -146,12 +144,13 @@ class CERES_EXPORT_INTERNAL Program { // fixed_cost will be equal to the sum of the costs of the residual // blocks that were removed. // - // If there was a problem, then the function will return a NULL + // If there was a problem, then the function will return a nullptr // pointer and error will contain a human readable description of // the problem. - Program* CreateReducedProgram(std::vector<double*>* removed_parameter_blocks, - double* fixed_cost, - std::string* error) const; + std::unique_ptr<Program> CreateReducedProgram( + std::vector<double*>* removed_parameter_blocks, + double* fixed_cost, + std::string* error) const; // See problem.h for what these do. int NumParameterBlocks() const; @@ -196,4 +195,6 @@ class CERES_EXPORT_INTERNAL Program { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_PROGRAM_H_ diff --git a/extern/ceres/internal/ceres/program_evaluator.h b/extern/ceres/internal/ceres/program_evaluator.h index 36c9c64baf6..826a73a9af1 100644 --- a/extern/ceres/internal/ceres/program_evaluator.h +++ b/extern/ceres/internal/ceres/program_evaluator.h @@ -59,11 +59,13 @@ // class JacobianWriter { // // Create a jacobian that this writer can write. Same as // // Evaluator::CreateJacobian. -// SparseMatrix* CreateJacobian() const; +// std::unique_ptr<SparseMatrix> CreateJacobian() const; // -// // Create num_threads evaluate preparers. Caller owns result which must -// // be freed with delete[]. Resulting preparers are valid while *this is. -// EvaluatePreparer* CreateEvaluatePreparers(int num_threads); +// // Create num_threads evaluate preparers.Resulting preparers are valid +// // while *this is. +// +// std::unique_ptr<EvaluatePreparer[]> CreateEvaluatePreparers( +// int num_threads); // // // Write the block jacobians from a residual block evaluation to the // // larger sparse jacobian. @@ -81,7 +83,7 @@ // This include must come before any #ifndef check on Ceres compile options. // clang-format off -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" // clang-format on #include <atomic> @@ -109,14 +111,14 @@ struct NullJacobianFinalizer { template <typename EvaluatePreparer, typename JacobianWriter, typename JacobianFinalizer = NullJacobianFinalizer> -class ProgramEvaluator : public Evaluator { +class ProgramEvaluator final : public Evaluator { public: ProgramEvaluator(const Evaluator::Options& options, Program* program) : options_(options), program_(program), jacobian_writer_(options, program), - evaluate_preparers_( - jacobian_writer_.CreateEvaluatePreparers(options.num_threads)) { + evaluate_preparers_(std::move( + jacobian_writer_.CreateEvaluatePreparers(options.num_threads))) { #ifdef CERES_NO_THREADS if (options_.num_threads > 1) { LOG(WARNING) << "No threading support is compiled into this binary; " @@ -127,12 +129,12 @@ class ProgramEvaluator : public Evaluator { #endif // CERES_NO_THREADS BuildResidualLayout(*program, &residual_layout_); - evaluate_scratch_.reset( - CreateEvaluatorScratch(*program, options.num_threads)); + evaluate_scratch_ = + std::move(CreateEvaluatorScratch(*program, options.num_threads)); } // Implementation of Evaluator interface. - SparseMatrix* CreateJacobian() const final { + std::unique_ptr<SparseMatrix> CreateJacobian() const final { return jacobian_writer_.CreateJacobian(); } @@ -250,7 +252,7 @@ class ProgramEvaluator : public Evaluator { MatrixTransposeVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>( block_jacobians[j], num_residuals, - parameter_block->LocalSize(), + parameter_block->TangentSize(), block_residuals, scratch->gradient.get() + parameter_block->delta_offset()); } @@ -309,18 +311,19 @@ class ProgramEvaluator : public Evaluator { int max_scratch_doubles_needed_for_evaluate, int max_residuals_per_residual_block, int num_parameters) { - residual_block_evaluate_scratch.reset( - new double[max_scratch_doubles_needed_for_evaluate]); - gradient.reset(new double[num_parameters]); + residual_block_evaluate_scratch = + std::make_unique<double[]>(max_scratch_doubles_needed_for_evaluate); + gradient = std::make_unique<double[]>(num_parameters); VectorRef(gradient.get(), num_parameters).setZero(); - residual_block_residuals.reset( - new double[max_residuals_per_residual_block]); - jacobian_block_ptrs.reset(new double*[max_parameters_per_residual_block]); + residual_block_residuals = + std::make_unique<double[]>(max_residuals_per_residual_block); + jacobian_block_ptrs = + std::make_unique<double*[]>(max_parameters_per_residual_block); } double cost; std::unique_ptr<double[]> residual_block_evaluate_scratch; - // The gradient in the local parameterization. + // The gradient on the manifold. std::unique_ptr<double[]> gradient; // Enough space to store the residual for the largest residual block. std::unique_ptr<double[]> residual_block_residuals; @@ -341,8 +344,8 @@ class ProgramEvaluator : public Evaluator { } // Create scratch space for each thread evaluating the program. - static EvaluateScratch* CreateEvaluatorScratch(const Program& program, - int num_threads) { + static std::unique_ptr<EvaluateScratch[]> CreateEvaluatorScratch( + const Program& program, int num_threads) { int max_parameters_per_residual_block = program.MaxParametersPerResidualBlock(); int max_scratch_doubles_needed_for_evaluate = @@ -351,7 +354,7 @@ class ProgramEvaluator : public Evaluator { program.MaxResidualsPerResidualBlock(); int num_parameters = program.NumEffectiveParameters(); - EvaluateScratch* evaluate_scratch = new EvaluateScratch[num_threads]; + auto evaluate_scratch = std::make_unique<EvaluateScratch[]>(num_threads); for (int i = 0; i < num_threads; i++) { evaluate_scratch[i].Init(max_parameters_per_residual_block, max_scratch_doubles_needed_for_evaluate, diff --git a/extern/ceres/internal/ceres/random.h b/extern/ceres/internal/ceres/random.h index 6b280f9ee64..0495d67581d 100644 --- a/extern/ceres/internal/ceres/random.h +++ b/extern/ceres/internal/ceres/random.h @@ -35,7 +35,7 @@ #include <cmath> #include <cstdlib> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { @@ -50,7 +50,7 @@ inline int Uniform(int n) { } inline double RandDouble() { - double r = static_cast<double>(rand()); + auto r = static_cast<double>(rand()); return r / RAND_MAX; } diff --git a/extern/ceres/internal/ceres/reorder_program.cc b/extern/ceres/internal/ceres/reorder_program.cc index 5d802365f33..d552ebf3de3 100644 --- a/extern/ceres/internal/ceres/reorder_program.cc +++ b/extern/ceres/internal/ceres/reorder_program.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -37,7 +37,8 @@ #include "Eigen/SparseCore" #include "ceres/cxsparse.h" -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" +#include "ceres/internal/export.h" #include "ceres/ordered_groups.h" #include "ceres/parameter_block.h" #include "ceres/parameter_block_ordering.h" @@ -88,8 +89,8 @@ static int MinParameterBlock(const ResidualBlock* residual_block, #if defined(CERES_USE_EIGEN_SPARSE) Eigen::SparseMatrix<int> CreateBlockJacobian( const TripletSparseMatrix& block_jacobian_transpose) { - typedef Eigen::SparseMatrix<int> SparseMatrix; - typedef Eigen::Triplet<int> Triplet; + using SparseMatrix = Eigen::SparseMatrix<int>; + using Triplet = Eigen::Triplet<int>; const int* rows = block_jacobian_transpose.rows(); const int* cols = block_jacobian_transpose.cols(); @@ -97,7 +98,7 @@ Eigen::SparseMatrix<int> CreateBlockJacobian( vector<Triplet> triplets; triplets.reserve(num_nonzeros); for (int i = 0; i < num_nonzeros; ++i) { - triplets.push_back(Triplet(cols[i], rows[i], 1)); + triplets.emplace_back(cols[i], rows[i], 1); } SparseMatrix block_jacobian(block_jacobian_transpose.num_cols(), @@ -127,9 +128,9 @@ void OrderingForSparseNormalCholeskyUsingSuiteSparse( ss.ApproximateMinimumDegreeOrdering(block_jacobian_transpose, &ordering[0]); } else { vector<int> constraints; - for (int i = 0; i < parameter_blocks.size(); ++i) { + for (auto* parameter_block : parameter_blocks) { constraints.push_back(parameter_block_ordering.GroupId( - parameter_blocks[i]->mutable_user_state())); + parameter_block->mutable_user_state())); } // Renumber the entries of constraints to be contiguous integers @@ -188,7 +189,7 @@ void OrderingForSparseNormalCholeskyUsingEigenSparse( // things. The right thing to do here would be to get a compressed // row sparse matrix representation of the jacobian and go from // there. But that is a project for another day. - typedef Eigen::SparseMatrix<int> SparseMatrix; + using SparseMatrix = Eigen::SparseMatrix<int>; const SparseMatrix block_jacobian = CreateBlockJacobian(tsm_block_jacobian_transpose); @@ -279,7 +280,7 @@ bool LexicographicallyOrderResidualBlocks( CHECK(find(residual_blocks_per_e_block.begin(), residual_blocks_per_e_block.end() - 1, - 0) != residual_blocks_per_e_block.end()) + 0) == residual_blocks_per_e_block.end() - 1) << "Congratulations, you found a Ceres bug! Please report this error " << "to the developers."; @@ -291,7 +292,7 @@ bool LexicographicallyOrderResidualBlocks( // filling is finished, the offset pointerts should have shifted down one // entry (this is verified below). vector<ResidualBlock*> reordered_residual_blocks( - (*residual_blocks).size(), static_cast<ResidualBlock*>(NULL)); + (*residual_blocks).size(), static_cast<ResidualBlock*>(nullptr)); for (int i = 0; i < residual_blocks->size(); ++i) { int bucket = min_position_per_residual[i]; @@ -299,7 +300,7 @@ bool LexicographicallyOrderResidualBlocks( offsets[bucket]--; // Sanity. - CHECK(reordered_residual_blocks[offsets[bucket]] == NULL) + CHECK(reordered_residual_blocks[offsets[bucket]] == nullptr) << "Congratulations, you found a Ceres bug! Please report this error " << "to the developers."; @@ -313,9 +314,9 @@ bool LexicographicallyOrderResidualBlocks( << "Congratulations, you found a Ceres bug! Please report this error " << "to the developers."; } - // Sanity check #2: No NULL's left behind. - for (int i = 0; i < reordered_residual_blocks.size(); ++i) { - CHECK(reordered_residual_blocks[i] != NULL) + // Sanity check #2: No nullptr's left behind. + for (auto* residual_block : reordered_residual_blocks) { + CHECK(residual_block != nullptr) << "Congratulations, you found a Ceres bug! Please report this error " << "to the developers."; } @@ -339,9 +340,9 @@ static void MaybeReorderSchurComplementColumnsUsingSuiteSparse( vector<ParameterBlock*>& parameter_blocks = *(program->mutable_parameter_blocks()); - for (int i = 0; i < parameter_blocks.size(); ++i) { + for (auto* parameter_block : parameter_blocks) { constraints.push_back(parameter_block_ordering.GroupId( - parameter_blocks[i]->mutable_user_state())); + parameter_block->mutable_user_state())); } // Renumber the entries of constraints to be contiguous integers as @@ -378,7 +379,7 @@ static void MaybeReorderSchurComplementColumnsUsingEigen( std::unique_ptr<TripletSparseMatrix> tsm_block_jacobian_transpose( program->CreateJacobianBlockSparsityTranspose()); - typedef Eigen::SparseMatrix<int> SparseMatrix; + using SparseMatrix = Eigen::SparseMatrix<int>; const SparseMatrix block_jacobian = CreateBlockJacobian(*tsm_block_jacobian_transpose); const int num_rows = block_jacobian.rows(); @@ -441,7 +442,7 @@ bool ReorderProgramForSchurTypeLinearSolver( if (parameter_block_ordering->NumGroups() == 1) { // If the user supplied an parameter_block_ordering with just one - // group, it is equivalent to the user supplying NULL as an + // group, it is equivalent to the user supplying nullptr as an // parameter_block_ordering. Ceres is completely free to choose the // parameter block ordering as it sees fit. For Schur type solvers, // this means that the user wishes for Ceres to identify the diff --git a/extern/ceres/internal/ceres/reorder_program.h b/extern/ceres/internal/ceres/reorder_program.h index 2e0c3264377..fbc49231c33 100644 --- a/extern/ceres/internal/ceres/reorder_program.h +++ b/extern/ceres/internal/ceres/reorder_program.h @@ -33,7 +33,8 @@ #include <string> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/parameter_block_ordering.h" #include "ceres/problem_impl.h" #include "ceres/types.h" @@ -44,7 +45,7 @@ namespace internal { class Program; // Reorder the parameter blocks in program using the ordering -CERES_EXPORT_INTERNAL bool ApplyOrdering( +CERES_NO_EXPORT bool ApplyOrdering( const ProblemImpl::ParameterMap& parameter_map, const ParameterBlockOrdering& ordering, Program* program, @@ -53,7 +54,7 @@ CERES_EXPORT_INTERNAL bool ApplyOrdering( // Reorder the residuals for program, if necessary, so that the residuals // involving each E block occur together. This is a necessary condition for the // Schur eliminator, which works on these "row blocks" in the jacobian. -CERES_EXPORT_INTERNAL bool LexicographicallyOrderResidualBlocks( +CERES_NO_EXPORT bool LexicographicallyOrderResidualBlocks( int size_of_first_elimination_group, Program* program, std::string* error); // Schur type solvers require that all parameter blocks eliminated @@ -72,7 +73,7 @@ CERES_EXPORT_INTERNAL bool LexicographicallyOrderResidualBlocks( // // Upon return, ordering contains the parameter block ordering that // was used to order the program. -CERES_EXPORT_INTERNAL bool ReorderProgramForSchurTypeLinearSolver( +CERES_NO_EXPORT bool ReorderProgramForSchurTypeLinearSolver( LinearSolverType linear_solver_type, SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type, const ProblemImpl::ParameterMap& parameter_map, @@ -90,7 +91,7 @@ CERES_EXPORT_INTERNAL bool ReorderProgramForSchurTypeLinearSolver( // fill-reducing ordering is available in the sparse linear algebra // library (SuiteSparse version >= 4.2.0) then the fill reducing // ordering will take it into account, otherwise it will be ignored. -CERES_EXPORT_INTERNAL bool ReorderProgramForSparseCholesky( +CERES_NO_EXPORT bool ReorderProgramForSparseCholesky( SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type, const ParameterBlockOrdering& parameter_block_ordering, int start_row_block, @@ -107,11 +108,13 @@ CERES_EXPORT_INTERNAL bool ReorderProgramForSparseCholesky( // bottom_residual_blocks.size() because we allow // bottom_residual_blocks to contain residual blocks not present in // the Program. -CERES_EXPORT_INTERNAL int ReorderResidualBlocksByPartition( +CERES_NO_EXPORT int ReorderResidualBlocksByPartition( const std::unordered_set<ResidualBlockId>& bottom_residual_blocks, Program* program); } // namespace internal } // namespace ceres -#endif // CERES_INTERNAL_REORDER_PROGRAM_ +#include "ceres/internal/reenable_warnings.h" + +#endif // CERES_INTERNAL_REORDER_PROGRAM_H_ diff --git a/extern/ceres/internal/ceres/residual_block.cc b/extern/ceres/internal/ceres/residual_block.cc index 067c9efe83d..cd408f2f98e 100644 --- a/extern/ceres/internal/ceres/residual_block.cc +++ b/extern/ceres/internal/ceres/residual_block.cc @@ -39,8 +39,8 @@ #include "ceres/cost_function.h" #include "ceres/internal/eigen.h" #include "ceres/internal/fixed_array.h" -#include "ceres/local_parameterization.h" #include "ceres/loss_function.h" +#include "ceres/manifold.h" #include "ceres/parameter_block.h" #include "ceres/residual_block_utils.h" #include "ceres/small_blas.h" @@ -87,7 +87,7 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function, for (int i = 0; i < num_parameter_blocks; ++i) { const ParameterBlock* parameter_block = parameter_blocks_[i]; if (jacobians[i] != nullptr && - parameter_block->LocalParameterizationJacobian() != nullptr) { + parameter_block->PlusJacobian() != nullptr) { global_jacobians[i] = scratch; scratch += num_residuals * parameter_block->Size(); } else { @@ -132,27 +132,27 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function, double squared_norm = VectorRef(residuals, num_residuals).squaredNorm(); - // Update the jacobians with the local parameterizations. + // Update the plus_jacobian for the manifolds. if (jacobians != nullptr) { for (int i = 0; i < num_parameter_blocks; ++i) { if (jacobians[i] != nullptr) { const ParameterBlock* parameter_block = parameter_blocks_[i]; - // Apply local reparameterization to the jacobians. - if (parameter_block->LocalParameterizationJacobian() != nullptr) { + // Apply the Manifold::PlusJacobian to the ambient jacobians. + if (parameter_block->PlusJacobian() != nullptr) { // jacobians[i] = global_jacobians[i] * global_to_local_jacobian. MatrixMatrixMultiply<Dynamic, Dynamic, Dynamic, Dynamic, 0>( global_jacobians[i], num_residuals, parameter_block->Size(), - parameter_block->LocalParameterizationJacobian(), + parameter_block->PlusJacobian(), parameter_block->Size(), - parameter_block->LocalSize(), + parameter_block->TangentSize(), jacobians[i], 0, 0, num_residuals, - parameter_block->LocalSize()); + parameter_block->TangentSize()); } } } @@ -183,7 +183,7 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function, // Correct the jacobians for the loss function. correct.CorrectJacobian(num_residuals, - parameter_block->LocalSize(), + parameter_block->TangentSize(), residuals, jacobians[i]); } @@ -199,16 +199,16 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function, int ResidualBlock::NumScratchDoublesForEvaluate() const { // Compute the amount of scratch space needed to store the full-sized - // jacobians. For parameters that have no local parameterization no storage - // is needed and the passed-in jacobian array is used directly. Also include - // space to store the residuals, which is needed for cost-only evaluations. - // This is slightly pessimistic, since both won't be needed all the time, but - // the amount of excess should not cause problems for the caller. + // jacobians. For parameters that have no manifold no storage is needed and + // the passed-in jacobian array is used directly. Also include space to store + // the residuals, which is needed for cost-only evaluations. This is slightly + // pessimistic, since both won't be needed all the time, but the amount of + // excess should not cause problems for the caller. int num_parameters = NumParameterBlocks(); int scratch_doubles = 1; for (int i = 0; i < num_parameters; ++i) { const ParameterBlock* parameter_block = parameter_blocks_[i]; - if (parameter_block->LocalParameterizationJacobian() != nullptr) { + if (parameter_block->PlusJacobian() != nullptr) { scratch_doubles += parameter_block->Size(); } } diff --git a/extern/ceres/internal/ceres/residual_block.h b/extern/ceres/internal/ceres/residual_block.h index f28fd42857c..978b94640fe 100644 --- a/extern/ceres/internal/ceres/residual_block.h +++ b/extern/ceres/internal/ceres/residual_block.h @@ -40,7 +40,8 @@ #include <vector> #include "ceres/cost_function.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/stringprintf.h" #include "ceres/types.h" @@ -65,7 +66,7 @@ class ParameterBlock; // // The residual block stores pointers to but does not own the cost functions, // loss functions, and parameter blocks. -class CERES_EXPORT_INTERNAL ResidualBlock { +class CERES_NO_EXPORT ResidualBlock { public: // Construct the residual block with the given cost/loss functions. Loss may // be null. The index is the index of the residual block in the Program's @@ -77,10 +78,10 @@ class CERES_EXPORT_INTERNAL ResidualBlock { // Evaluates the residual term, storing the scalar cost in *cost, the residual // components in *residuals, and the jacobians between the parameters and - // residuals in jacobians[i], in row-major order. If residuals is NULL, the - // residuals are not computed. If jacobians is NULL, no jacobians are - // computed. If jacobians[i] is NULL, then the jacobian for that parameter is - // not computed. + // residuals in jacobians[i], in row-major order. If residuals is nullptr, the + // residuals are not computed. If jacobians is nullptr, no jacobians are + // computed. If jacobians[i] is nullptr, then the jacobian for that parameter + // is not computed. // // cost must not be null. // @@ -92,10 +93,10 @@ class CERES_EXPORT_INTERNAL ResidualBlock { // false, the caller should expect the output memory locations to have // been modified. // - // The returned cost and jacobians have had robustification and local - // parameterizations applied already; for example, the jacobian for a - // 4-dimensional quaternion parameter using the "QuaternionParameterization" - // is num_residuals by 3 instead of num_residuals by 4. + // The returned cost and jacobians have had robustification and manifold + // projection applied already; for example, the jacobian for a 4-dimensional + // quaternion parameter using the "Quaternion" manifold is num_residuals by 3 + // instead of num_residuals by 4. // // apply_loss_function as the name implies allows the user to switch // the application of the loss function on and off. @@ -147,4 +148,6 @@ class CERES_EXPORT_INTERNAL ResidualBlock { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_RESIDUAL_BLOCK_H_ diff --git a/extern/ceres/internal/ceres/residual_block_utils.cc b/extern/ceres/internal/ceres/residual_block_utils.cc index d5b3fa1fa37..11c7623ce22 100644 --- a/extern/ceres/internal/ceres/residual_block_utils.cc +++ b/extern/ceres/internal/ceres/residual_block_utils.cc @@ -36,7 +36,7 @@ #include "ceres/array_utils.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/parameter_block.h" #include "ceres/residual_block.h" #include "ceres/stringprintf.h" @@ -56,7 +56,7 @@ void InvalidateEvaluation(const ResidualBlock& block, InvalidateArray(1, cost); InvalidateArray(num_residuals, residuals); - if (jacobians != NULL) { + if (jacobians != nullptr) { for (int i = 0; i < num_parameter_blocks; ++i) { const int parameter_block_size = block.parameter_blocks()[i]->Size(); InvalidateArray(num_residuals * parameter_block_size, jacobians[i]); @@ -104,9 +104,9 @@ string EvaluationToString(const ResidualBlock& block, StringAppendF(&result, "| "); for (int k = 0; k < num_residuals; ++k) { AppendArrayToString(1, - (jacobians != NULL && jacobians[i] != NULL) + (jacobians != nullptr && jacobians[i] != nullptr) ? jacobians[i] + k * parameter_block_size + j - : NULL, + : nullptr, &result); } StringAppendF(&result, "\n"); @@ -129,7 +129,7 @@ bool IsEvaluationValid(const ResidualBlock& block, return false; } - if (jacobians != NULL) { + if (jacobians != nullptr) { for (int i = 0; i < num_parameter_blocks; ++i) { const int parameter_block_size = block.parameter_blocks()[i]->Size(); if (!IsArrayValid(num_residuals * parameter_block_size, jacobians[i])) { diff --git a/extern/ceres/internal/ceres/residual_block_utils.h b/extern/ceres/internal/ceres/residual_block_utils.h index 41ae81abc99..f75b6aecce9 100644 --- a/extern/ceres/internal/ceres/residual_block_utils.h +++ b/extern/ceres/internal/ceres/residual_block_utils.h @@ -45,14 +45,15 @@ #include <string> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { class ResidualBlock; -// Invalidate cost, resdual and jacobian arrays (if not NULL). +// Invalidate cost, resdual and jacobian arrays (if not nullptr). +CERES_NO_EXPORT void InvalidateEvaluation(const ResidualBlock& block, double* cost, double* residuals, @@ -60,6 +61,7 @@ void InvalidateEvaluation(const ResidualBlock& block, // Check if any of the arrays cost, residuals or jacobians contains an // NaN, return true if it does. +CERES_NO_EXPORT bool IsEvaluationValid(const ResidualBlock& block, double const* const* parameters, double* cost, @@ -69,6 +71,7 @@ bool IsEvaluationValid(const ResidualBlock& block, // Create a string representation of the Residual block containing the // value of the parameters, residuals and jacobians if present. // Useful for debugging output. +CERES_NO_EXPORT std::string EvaluationToString(const ResidualBlock& block, double const* const* parameters, double* cost, diff --git a/extern/ceres/internal/ceres/schur_complement_solver.cc b/extern/ceres/internal/ceres/schur_complement_solver.cc index 65e7854f9e5..bb442b4280b 100644 --- a/extern/ceres/internal/ceres/schur_complement_solver.cc +++ b/extern/ceres/internal/ceres/schur_complement_solver.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -46,7 +46,6 @@ #include "ceres/conjugate_gradients_solver.h" #include "ceres/detect_structure.h" #include "ceres/internal/eigen.h" -#include "ceres/lapack.h" #include "ceres/linear_solver.h" #include "ceres/sparse_cholesky.h" #include "ceres/triplet_sparse_matrix.h" @@ -63,14 +62,12 @@ using std::vector; namespace { -class BlockRandomAccessSparseMatrixAdapter : public LinearOperator { +class BlockRandomAccessSparseMatrixAdapter final : public LinearOperator { public: explicit BlockRandomAccessSparseMatrixAdapter( const BlockRandomAccessSparseMatrix& m) : m_(m) {} - virtual ~BlockRandomAccessSparseMatrixAdapter() {} - // y = y + Ax; void RightMultiply(const double* x, double* y) const final { m_.SymmetricRightMultiply(x, y); @@ -88,14 +85,12 @@ class BlockRandomAccessSparseMatrixAdapter : public LinearOperator { const BlockRandomAccessSparseMatrix& m_; }; -class BlockRandomAccessDiagonalMatrixAdapter : public LinearOperator { +class BlockRandomAccessDiagonalMatrixAdapter final : public LinearOperator { public: explicit BlockRandomAccessDiagonalMatrixAdapter( const BlockRandomAccessDiagonalMatrix& m) : m_(m) {} - virtual ~BlockRandomAccessDiagonalMatrixAdapter() {} - // y = y + Ax; void RightMultiply(const double* x, double* y) const final { m_.RightMultiply(x, y); @@ -115,6 +110,14 @@ class BlockRandomAccessDiagonalMatrixAdapter : public LinearOperator { } // namespace +SchurComplementSolver::SchurComplementSolver( + const LinearSolver::Options& options) + : options_(options) { + CHECK_GT(options.elimination_groups.size(), 1); + CHECK_GT(options.elimination_groups[0], 0); + CHECK(options.context != nullptr); +} + LinearSolver::Summary SchurComplementSolver::SolveImpl( BlockSparseMatrix* A, const double* b, @@ -123,7 +126,7 @@ LinearSolver::Summary SchurComplementSolver::SolveImpl( EventLogger event_logger("SchurComplementSolver::Solve"); const CompressedRowBlockStructure* bs = A->block_structure(); - if (eliminator_.get() == NULL) { + if (eliminator_.get() == nullptr) { const int num_eliminate_blocks = options_.elimination_groups[0]; const int num_f_blocks = bs->cols.size() - num_eliminate_blocks; @@ -141,9 +144,9 @@ LinearSolver::Summary SchurComplementSolver::SolveImpl( // mechanism that does not cause binary bloat. if (options_.row_block_size == 2 && options_.e_block_size == 3 && options_.f_block_size == 6 && num_f_blocks == 1) { - eliminator_.reset(new SchurEliminatorForOneFBlock<2, 3, 6>); + eliminator_ = std::make_unique<SchurEliminatorForOneFBlock<2, 3, 6>>(); } else { - eliminator_.reset(SchurEliminatorBase::Create(options_)); + eliminator_ = SchurEliminatorBase::Create(options_); } CHECK(eliminator_); @@ -174,6 +177,12 @@ LinearSolver::Summary SchurComplementSolver::SolveImpl( return summary; } +DenseSchurComplementSolver::DenseSchurComplementSolver( + const LinearSolver::Options& options) + : SchurComplementSolver(options), + cholesky_(DenseCholesky::Create(options)) {} + +DenseSchurComplementSolver::~DenseSchurComplementSolver() = default; // Initialize a BlockRandomAccessDenseMatrix to store the Schur // complement. @@ -187,8 +196,8 @@ void DenseSchurComplementSolver::InitStorage( blocks[j] = bs->cols[i].size; } - set_lhs(new BlockRandomAccessDenseMatrix(blocks)); - set_rhs(new double[lhs()->num_rows()]); + set_lhs(std::make_unique<BlockRandomAccessDenseMatrix>(blocks)); + set_rhs(std::make_unique<double[]>(lhs()->num_rows())); } // Solve the system Sx = r, assuming that the matrix S is stored in a @@ -201,8 +210,7 @@ LinearSolver::Summary DenseSchurComplementSolver::SolveReducedLinearSystem( summary.termination_type = LINEAR_SOLVER_SUCCESS; summary.message = "Success."; - const BlockRandomAccessDenseMatrix* m = - down_cast<const BlockRandomAccessDenseMatrix*>(lhs()); + auto* m = down_cast<BlockRandomAccessDenseMatrix*>(mutable_lhs()); const int num_rows = m->num_rows(); // The case where there are no f blocks, and the system is block @@ -212,26 +220,8 @@ LinearSolver::Summary DenseSchurComplementSolver::SolveReducedLinearSystem( } summary.num_iterations = 1; - - if (options().dense_linear_algebra_library_type == EIGEN) { - Eigen::LLT<Matrix, Eigen::Upper> llt = - ConstMatrixRef(m->values(), num_rows, num_rows) - .selfadjointView<Eigen::Upper>() - .llt(); - if (llt.info() != Eigen::Success) { - summary.termination_type = LINEAR_SOLVER_FAILURE; - summary.message = - "Eigen failure. Unable to perform dense Cholesky factorization."; - return summary; - } - - VectorRef(solution, num_rows) = llt.solve(ConstVectorRef(rhs(), num_rows)); - } else { - VectorRef(solution, num_rows) = ConstVectorRef(rhs(), num_rows); - summary.termination_type = LAPACK::SolveInPlaceUsingCholesky( - num_rows, m->values(), solution, &summary.message); - } - + summary.termination_type = cholesky_->FactorAndSolve( + num_rows, m->mutable_values(), rhs(), solution, &summary.message); return summary; } @@ -243,7 +233,7 @@ SparseSchurComplementSolver::SparseSchurComplementSolver( } } -SparseSchurComplementSolver::~SparseSchurComplementSolver() {} +SparseSchurComplementSolver::~SparseSchurComplementSolver() = default; // Determine the non-zero blocks in the Schur Complement matrix, and // initialize a BlockRandomAccessSparseMatrix object. @@ -303,8 +293,8 @@ void SparseSchurComplementSolver::InitStorage( CHECK_GE(row.cells.front().block_id, num_eliminate_blocks); for (int i = 0; i < row.cells.size(); ++i) { int r_block1_id = row.cells[i].block_id - num_eliminate_blocks; - for (int j = 0; j < row.cells.size(); ++j) { - int r_block2_id = row.cells[j].block_id - num_eliminate_blocks; + for (const auto& cell : row.cells) { + int r_block2_id = cell.block_id - num_eliminate_blocks; if (r_block1_id <= r_block2_id) { block_pairs.insert(make_pair(r_block1_id, r_block2_id)); } @@ -312,8 +302,9 @@ void SparseSchurComplementSolver::InitStorage( } } - set_lhs(new BlockRandomAccessSparseMatrix(blocks_, block_pairs)); - set_rhs(new double[lhs()->num_rows()]); + set_lhs( + std::make_unique<BlockRandomAccessSparseMatrix>(blocks_, block_pairs)); + set_rhs(std::make_unique<double[]>(lhs()->num_rows())); } LinearSolver::Summary SparseSchurComplementSolver::SolveReducedLinearSystem( @@ -338,11 +329,10 @@ LinearSolver::Summary SparseSchurComplementSolver::SolveReducedLinearSystem( const CompressedRowSparseMatrix::StorageType storage_type = sparse_cholesky_->StorageType(); if (storage_type == CompressedRowSparseMatrix::UPPER_TRIANGULAR) { - lhs.reset(CompressedRowSparseMatrix::FromTripletSparseMatrix(*tsm)); + lhs = CompressedRowSparseMatrix::FromTripletSparseMatrix(*tsm); lhs->set_storage_type(CompressedRowSparseMatrix::UPPER_TRIANGULAR); } else { - lhs.reset( - CompressedRowSparseMatrix::FromTripletSparseMatrixTransposed(*tsm)); + lhs = CompressedRowSparseMatrix::FromTripletSparseMatrixTransposed(*tsm); lhs->set_storage_type(CompressedRowSparseMatrix::LOWER_TRIANGULAR); } @@ -373,12 +363,12 @@ SparseSchurComplementSolver::SolveReducedLinearSystemUsingConjugateGradients( // Only SCHUR_JACOBI is supported over here right now. CHECK_EQ(options().preconditioner_type, SCHUR_JACOBI); - if (preconditioner_.get() == NULL) { - preconditioner_.reset(new BlockRandomAccessDiagonalMatrix(blocks_)); + if (preconditioner_.get() == nullptr) { + preconditioner_ = + std::make_unique<BlockRandomAccessDiagonalMatrix>(blocks_); } - BlockRandomAccessSparseMatrix* sc = down_cast<BlockRandomAccessSparseMatrix*>( - const_cast<BlockRandomAccessMatrix*>(lhs())); + auto* sc = down_cast<BlockRandomAccessSparseMatrix*>(mutable_lhs()); // Extract block diagonal from the Schur complement to construct the // schur_jacobi preconditioner. @@ -404,10 +394,11 @@ SparseSchurComplementSolver::SolveReducedLinearSystemUsingConjugateGradients( VectorRef(solution, num_rows).setZero(); - std::unique_ptr<LinearOperator> lhs_adapter( - new BlockRandomAccessSparseMatrixAdapter(*sc)); - std::unique_ptr<LinearOperator> preconditioner_adapter( - new BlockRandomAccessDiagonalMatrixAdapter(*preconditioner_)); + std::unique_ptr<LinearOperator> lhs_adapter = + std::make_unique<BlockRandomAccessSparseMatrixAdapter>(*sc); + std::unique_ptr<LinearOperator> preconditioner_adapter = + std::make_unique<BlockRandomAccessDiagonalMatrixAdapter>( + *preconditioner_); LinearSolver::Options cg_options; cg_options.min_num_iterations = options().min_num_iterations; diff --git a/extern/ceres/internal/ceres/schur_complement_solver.h b/extern/ceres/internal/ceres/schur_complement_solver.h index 3bfa22f22e4..859a086cdf4 100644 --- a/extern/ceres/internal/ceres/schur_complement_solver.h +++ b/extern/ceres/internal/ceres/schur_complement_solver.h @@ -40,7 +40,9 @@ #include "ceres/block_random_access_matrix.h" #include "ceres/block_sparse_matrix.h" #include "ceres/block_structure.h" -#include "ceres/internal/port.h" +#include "ceres/dense_cholesky.h" +#include "ceres/internal/config.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" #include "ceres/schur_eliminator.h" #include "ceres/types.h" @@ -50,6 +52,8 @@ #include "Eigen/SparseCholesky" #endif +#include "ceres/internal/disable_warnings.h" + namespace ceres { namespace internal { @@ -107,20 +111,12 @@ class SparseCholesky; // set to DENSE_SCHUR and SPARSE_SCHUR // respectively. LinearSolver::Options::elimination_groups[0] should // be at least 1. -class CERES_EXPORT_INTERNAL SchurComplementSolver - : public BlockSparseMatrixSolver { +class CERES_NO_EXPORT SchurComplementSolver : public BlockSparseMatrixSolver { public: - explicit SchurComplementSolver(const LinearSolver::Options& options) - : options_(options) { - CHECK_GT(options.elimination_groups.size(), 1); - CHECK_GT(options.elimination_groups[0], 0); - CHECK(options.context != NULL); - } + explicit SchurComplementSolver(const LinearSolver::Options& options); SchurComplementSolver(const SchurComplementSolver&) = delete; void operator=(const SchurComplementSolver&) = delete; - // LinearSolver methods - virtual ~SchurComplementSolver() {} LinearSolver::Summary SolveImpl( BlockSparseMatrix* A, const double* b, @@ -130,10 +126,14 @@ class CERES_EXPORT_INTERNAL SchurComplementSolver protected: const LinearSolver::Options& options() const { return options_; } + void set_lhs(std::unique_ptr<BlockRandomAccessMatrix> lhs) { + lhs_ = std::move(lhs); + } const BlockRandomAccessMatrix* lhs() const { return lhs_.get(); } - void set_lhs(BlockRandomAccessMatrix* lhs) { lhs_.reset(lhs); } + BlockRandomAccessMatrix* mutable_lhs() { return lhs_.get(); } + + void set_rhs(std::unique_ptr<double[]> rhs) { rhs_ = std::move(rhs); } const double* rhs() const { return rhs_.get(); } - void set_rhs(double* rhs) { rhs_.reset(rhs); } private: virtual void InitStorage(const CompressedRowBlockStructure* bs) = 0; @@ -149,30 +149,33 @@ class CERES_EXPORT_INTERNAL SchurComplementSolver }; // Dense Cholesky factorization based solver. -class DenseSchurComplementSolver : public SchurComplementSolver { +class CERES_NO_EXPORT DenseSchurComplementSolver final + : public SchurComplementSolver { public: - explicit DenseSchurComplementSolver(const LinearSolver::Options& options) - : SchurComplementSolver(options) {} + explicit DenseSchurComplementSolver(const LinearSolver::Options& options); DenseSchurComplementSolver(const DenseSchurComplementSolver&) = delete; void operator=(const DenseSchurComplementSolver&) = delete; - virtual ~DenseSchurComplementSolver() {} + ~DenseSchurComplementSolver() override; private: void InitStorage(const CompressedRowBlockStructure* bs) final; LinearSolver::Summary SolveReducedLinearSystem( const LinearSolver::PerSolveOptions& per_solve_options, double* solution) final; + + std::unique_ptr<DenseCholesky> cholesky_; }; // Sparse Cholesky factorization based solver. -class SparseSchurComplementSolver : public SchurComplementSolver { +class CERES_NO_EXPORT SparseSchurComplementSolver final + : public SchurComplementSolver { public: explicit SparseSchurComplementSolver(const LinearSolver::Options& options); SparseSchurComplementSolver(const SparseSchurComplementSolver&) = delete; void operator=(const SparseSchurComplementSolver&) = delete; - virtual ~SparseSchurComplementSolver(); + ~SparseSchurComplementSolver() override; private: void InitStorage(const CompressedRowBlockStructure* bs) final; @@ -191,4 +194,6 @@ class SparseSchurComplementSolver : public SchurComplementSolver { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_SCHUR_COMPLEMENT_SOLVER_H_ diff --git a/extern/ceres/internal/ceres/schur_eliminator.cc b/extern/ceres/internal/ceres/schur_eliminator.cc index 613ae9558e9..22e7358070f 100644 --- a/extern/ceres/internal/ceres/schur_eliminator.cc +++ b/extern/ceres/internal/ceres/schur_eliminator.cc @@ -39,121 +39,126 @@ // // This file is generated using generate_template_specializations.py. +#include <memory> + #include "ceres/linear_solver.h" #include "ceres/schur_eliminator.h" namespace ceres { namespace internal { -SchurEliminatorBase* SchurEliminatorBase::Create( +SchurEliminatorBase::~SchurEliminatorBase() = default; + +std::unique_ptr<SchurEliminatorBase> SchurEliminatorBase::Create( const LinearSolver::Options& options) { #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION if ((options.row_block_size == 2) && (options.e_block_size == 2) && (options.f_block_size == 2)) { - return new SchurEliminator<2, 2, 2>(options); + return std::make_unique<SchurEliminator<2, 2, 2>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 2) && (options.f_block_size == 3)) { - return new SchurEliminator<2, 2, 3>(options); + return std::make_unique<SchurEliminator<2, 2, 3>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 2) && (options.f_block_size == 4)) { - return new SchurEliminator<2, 2, 4>(options); + return std::make_unique<SchurEliminator<2, 2, 4>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 2)) { - return new SchurEliminator<2, 2, Eigen::Dynamic>(options); + return std::make_unique<SchurEliminator<2, 2, Eigen::Dynamic>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 3) && (options.f_block_size == 3)) { - return new SchurEliminator<2, 3, 3>(options); + return std::make_unique<SchurEliminator<2, 3, 3>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 3) && (options.f_block_size == 4)) { - return new SchurEliminator<2, 3, 4>(options); + return std::make_unique<SchurEliminator<2, 3, 4>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 3) && (options.f_block_size == 6)) { - return new SchurEliminator<2, 3, 6>(options); + return std::make_unique<SchurEliminator<2, 3, 6>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 3) && (options.f_block_size == 9)) { - return new SchurEliminator<2, 3, 9>(options); + return std::make_unique<SchurEliminator<2, 3, 9>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 3)) { - return new SchurEliminator<2, 3, Eigen::Dynamic>(options); + return std::make_unique<SchurEliminator<2, 3, Eigen::Dynamic>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 3)) { - return new SchurEliminator<2, 4, 3>(options); + return std::make_unique<SchurEliminator<2, 4, 3>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 4)) { - return new SchurEliminator<2, 4, 4>(options); + return std::make_unique<SchurEliminator<2, 4, 4>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 6)) { - return new SchurEliminator<2, 4, 6>(options); + return std::make_unique<SchurEliminator<2, 4, 6>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 8)) { - return new SchurEliminator<2, 4, 8>(options); + return std::make_unique<SchurEliminator<2, 4, 8>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 9)) { - return new SchurEliminator<2, 4, 9>(options); + return std::make_unique<SchurEliminator<2, 4, 9>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 4)) { - return new SchurEliminator<2, 4, Eigen::Dynamic>(options); + return std::make_unique<SchurEliminator<2, 4, Eigen::Dynamic>>(options); } if (options.row_block_size == 2) { - return new SchurEliminator<2, Eigen::Dynamic, Eigen::Dynamic>(options); + return std::make_unique<SchurEliminator<2, Eigen::Dynamic, Eigen::Dynamic>>(options); } if ((options.row_block_size == 3) && (options.e_block_size == 3) && (options.f_block_size == 3)) { - return new SchurEliminator<3, 3, 3>(options); + return std::make_unique<SchurEliminator<3, 3, 3>>(options); } if ((options.row_block_size == 4) && (options.e_block_size == 4) && (options.f_block_size == 2)) { - return new SchurEliminator<4, 4, 2>(options); + return std::make_unique<SchurEliminator<4, 4, 2>>(options); } if ((options.row_block_size == 4) && (options.e_block_size == 4) && (options.f_block_size == 3)) { - return new SchurEliminator<4, 4, 3>(options); + return std::make_unique<SchurEliminator<4, 4, 3>>(options); } if ((options.row_block_size == 4) && (options.e_block_size == 4) && (options.f_block_size == 4)) { - return new SchurEliminator<4, 4, 4>(options); + return std::make_unique<SchurEliminator<4, 4, 4>>(options); } if ((options.row_block_size == 4) && (options.e_block_size == 4)) { - return new SchurEliminator<4, 4, Eigen::Dynamic>(options); + return std::make_unique<SchurEliminator<4, 4, Eigen::Dynamic>>(options); } #endif VLOG(1) << "Template specializations not found for <" << options.row_block_size << "," << options.e_block_size << "," << options.f_block_size << ">"; - return new SchurEliminator<Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic>( - options); + return std::make_unique<SchurEliminator<Eigen::Dynamic, + Eigen::Dynamic, + Eigen::Dynamic>>(options); } } // namespace internal diff --git a/extern/ceres/internal/ceres/schur_eliminator.h b/extern/ceres/internal/ceres/schur_eliminator.h index 42c016ee9b0..91831dceb5a 100644 --- a/extern/ceres/internal/ceres/schur_eliminator.h +++ b/extern/ceres/internal/ceres/schur_eliminator.h @@ -40,8 +40,10 @@ #include "ceres/block_random_access_matrix.h" #include "ceres/block_sparse_matrix.h" #include "ceres/block_structure.h" +#include "ceres/internal/config.h" +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { @@ -56,9 +58,8 @@ namespace internal { // Where x = [y;z] is a partition of the variables. The partitioning // of the variables is such that, E'E is a block diagonal matrix. Or // in other words, the parameter blocks in E form an independent set -// of the of the graph implied by the block matrix A'A. Then, this -// class provides the functionality to compute the Schur complement -// system +// of the graph implied by the block matrix A'A. Then, this class +// provides the functionality to compute the Schur complement system // // S z = r // @@ -164,9 +165,9 @@ namespace internal { // 2008 for an example of such use]. // // Example usage: Please see schur_complement_solver.cc -class CERES_EXPORT_INTERNAL SchurEliminatorBase { +class CERES_NO_EXPORT SchurEliminatorBase { public: - virtual ~SchurEliminatorBase() {} + virtual ~SchurEliminatorBase(); // Initialize the eliminator. It is the user's responsibilty to call // this function before calling Eliminate or BackSubstitute. It is @@ -210,7 +211,8 @@ class CERES_EXPORT_INTERNAL SchurEliminatorBase { const double* z, double* y) = 0; // Factory - static SchurEliminatorBase* Create(const LinearSolver::Options& options); + static std::unique_ptr<SchurEliminatorBase> Create( + const LinearSolver::Options& options); }; // Templated implementation of the SchurEliminatorBase interface. The @@ -223,7 +225,7 @@ class CERES_EXPORT_INTERNAL SchurEliminatorBase { template <int kRowBlockSize = Eigen::Dynamic, int kEBlockSize = Eigen::Dynamic, int kFBlockSize = Eigen::Dynamic> -class SchurEliminator : public SchurEliminatorBase { +class CERES_NO_EXPORT SchurEliminator final : public SchurEliminatorBase { public: explicit SchurEliminator(const LinearSolver::Options& options) : num_threads_(options.num_threads), context_(options.context) { @@ -231,7 +233,7 @@ class SchurEliminator : public SchurEliminatorBase { } // SchurEliminatorBase Interface - virtual ~SchurEliminator(); + ~SchurEliminator() override; void Init(int num_eliminate_blocks, bool assume_full_rank_ete, const CompressedRowBlockStructure* bs) final; @@ -272,9 +274,9 @@ class SchurEliminator : public SchurEliminatorBase { // buffer_layout[z1] = 0 // buffer_layout[z5] = y1 * z1 // buffer_layout[z2] = y1 * z1 + y1 * z5 - typedef std::map<int, int> BufferLayoutType; + using BufferLayoutType = std::map<int, int>; struct Chunk { - Chunk() : size(0) {} + explicit Chunk(int start) : size(0), start(start) {} int size; int start; BufferLayoutType buffer_layout; @@ -378,9 +380,9 @@ class SchurEliminator : public SchurEliminatorBase { template <int kRowBlockSize = Eigen::Dynamic, int kEBlockSize = Eigen::Dynamic, int kFBlockSize = Eigen::Dynamic> -class SchurEliminatorForOneFBlock : public SchurEliminatorBase { +class CERES_NO_EXPORT SchurEliminatorForOneFBlock final + : public SchurEliminatorBase { public: - virtual ~SchurEliminatorForOneFBlock() {} void Init(int num_eliminate_blocks, bool assume_full_rank_ete, const CompressedRowBlockStructure* bs) override { @@ -484,7 +486,7 @@ class SchurEliminatorForOneFBlock : public SchurEliminatorBase { Eigen::Matrix<double, kFBlockSize, 1> f_t_b; // Add the square of the diagonal to e_t_e. - if (D != NULL) { + if (D != nullptr) { const typename EigenTypes<kEBlockSize>::ConstVectorRef diag( D + bs->cols[e_block_id].position, kEBlockSize); e_t_e = diag.array().square().matrix().asDiagonal(); @@ -624,4 +626,6 @@ class SchurEliminatorForOneFBlock : public SchurEliminatorBase { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_SCHUR_ELIMINATOR_H_ diff --git a/extern/ceres/internal/ceres/schur_eliminator_impl.h b/extern/ceres/internal/ceres/schur_eliminator_impl.h index 1f0b4fa481d..de3ba3e5dcb 100644 --- a/extern/ceres/internal/ceres/schur_eliminator_impl.h +++ b/extern/ceres/internal/ceres/schur_eliminator_impl.h @@ -47,7 +47,7 @@ // This include must come before any #ifndef check on Ceres compile options. // clang-format off -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" // clang-format on #include <algorithm> @@ -125,10 +125,8 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::Init( break; } - chunks_.push_back(Chunk()); + chunks_.push_back(Chunk(r)); Chunk& chunk = chunks_.back(); - chunk.size = 0; - chunk.start = r; int buffer_size = 0; const int e_block_size = bs->cols[chunk_block_id].size; @@ -161,12 +159,13 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::Init( uneliminated_row_begins_ = chunk.start + chunk.size; - buffer_.reset(new double[buffer_size_ * num_threads_]); + buffer_ = std::make_unique<double[]>(buffer_size_ * num_threads_); // chunk_outer_product_buffer_ only needs to store e_block_size * // f_block_size, which is always less than buffer_size_, so we just // allocate buffer_size_ per thread. - chunk_outer_product_buffer_.reset(new double[buffer_size_ * num_threads_]); + chunk_outer_product_buffer_ = + std::make_unique<double[]>(buffer_size_ * num_threads_); STLDeleteElements(&rhs_locks_); rhs_locks_.resize(num_col_blocks - num_eliminate_blocks_); @@ -193,7 +192,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::Eliminate( const int num_col_blocks = bs->cols.size(); // Add the diagonal to the schur complement. - if (D != NULL) { + if (D != nullptr) { ParallelFor(context_, num_eliminate_blocks_, num_col_blocks, @@ -203,7 +202,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::Eliminate( int r, c, row_stride, col_stride; CellInfo* cell_info = lhs->GetCell( block_id, block_id, &r, &c, &row_stride, &col_stride); - if (cell_info != NULL) { + if (cell_info != nullptr) { const int block_size = bs->cols[i].size; typename EigenTypes<Eigen::Dynamic>::ConstVectorRef diag( D + bs->cols[i].position, block_size); @@ -245,7 +244,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::Eliminate( typename EigenTypes<kEBlockSize, kEBlockSize>::Matrix ete(e_block_size, e_block_size); - if (D != NULL) { + if (D != nullptr) { const typename EigenTypes<kEBlockSize>::ConstVectorRef diag( D + bs->cols[e_block_id].position, e_block_size); ete = diag.array().square().matrix().asDiagonal(); @@ -327,7 +326,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::BackSubstitute( typename EigenTypes<kEBlockSize, kEBlockSize>::Matrix ete(e_block_size, e_block_size); - if (D != NULL) { + if (D != nullptr) { const typename EigenTypes<kEBlockSize>::ConstVectorRef diag( D + bs->cols[e_block_id].position, e_block_size); ete = diag.array().square().matrix().asDiagonal(); @@ -525,7 +524,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>:: // computation of the right-hand matrix product, but memory // references to the left hand side. const int e_block_size = inverse_ete.rows(); - BufferLayoutType::const_iterator it1 = buffer_layout.begin(); + auto it1 = buffer_layout.begin(); double* b1_transpose_inverse_ete = chunk_outer_product_buffer_.get() + thread_id * buffer_size_; @@ -542,14 +541,14 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>:: b1_transpose_inverse_ete, 0, 0, block1_size, e_block_size); // clang-format on - BufferLayoutType::const_iterator it2 = it1; + auto it2 = it1; for (; it2 != buffer_layout.end(); ++it2) { const int block2 = it2->first - num_eliminate_blocks_; int r, c, row_stride, col_stride; CellInfo* cell_info = lhs->GetCell(block1, block2, &r, &c, &row_stride, &col_stride); - if (cell_info != NULL) { + if (cell_info != nullptr) { const int block2_size = bs->cols[it2->first].size; std::lock_guard<std::mutex> l(cell_info->m); // clang-format off @@ -627,7 +626,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>:: int r, c, row_stride, col_stride; CellInfo* cell_info = lhs->GetCell(block1, block1, &r, &c, &row_stride, &col_stride); - if (cell_info != NULL) { + if (cell_info != nullptr) { std::lock_guard<std::mutex> l(cell_info->m); // This multiply currently ignores the fact that this is a // symmetric outer product. @@ -647,7 +646,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>:: int r, c, row_stride, col_stride; CellInfo* cell_info = lhs->GetCell(block1, block2, &r, &c, &row_stride, &col_stride); - if (cell_info != NULL) { + if (cell_info != nullptr) { const int block2_size = bs->cols[row.cells[j].block_id].size; std::lock_guard<std::mutex> l(cell_info->m); // clang-format off @@ -682,7 +681,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>:: int r, c, row_stride, col_stride; CellInfo* cell_info = lhs->GetCell(block1, block1, &r, &c, &row_stride, &col_stride); - if (cell_info != NULL) { + if (cell_info != nullptr) { std::lock_guard<std::mutex> l(cell_info->m); // block += b1.transpose() * b1; // clang-format off @@ -702,7 +701,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>:: int r, c, row_stride, col_stride; CellInfo* cell_info = lhs->GetCell(block1, block2, &r, &c, &row_stride, &col_stride); - if (cell_info != NULL) { + if (cell_info != nullptr) { // block += b1.transpose() * b2; std::lock_guard<std::mutex> l(cell_info->m); // clang-format off diff --git a/extern/ceres/internal/ceres/schur_eliminator_template.py b/extern/ceres/internal/ceres/schur_eliminator_template.py deleted file mode 100644 index 50515956e89..00000000000 --- a/extern/ceres/internal/ceres/schur_eliminator_template.py +++ /dev/null @@ -1,151 +0,0 @@ -# Ceres Solver - A fast non-linear least squares minimizer -# Copyright 2017 Google Inc. All rights reserved. -# http://ceres-solver.org/ -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Google Inc. nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# Author: sameeragarwal@google.com (Sameer Agarwal) -# -# Script for explicitly generating template specialization of the -# SchurEliminator class. It is a rather large class -# and the number of explicit instantiations is also large. Explicitly -# generating these instantiations in separate .cc files breaks the -# compilation into separate compilation unit rather than one large cc -# file which takes 2+GB of RAM to compile. -# -# This script creates two sets of files. -# -# 1. schur_eliminator_x_x_x.cc -# where, the x indicates the template parameters and -# -# 2. schur_eliminator.cc -# -# that contains a factory function for instantiating these classes -# based on runtime parameters. -# -# The list of tuples, specializations indicates the set of -# specializations that is generated. - -# Set of template specializations to generate - -HEADER = """// Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2017 Google Inc. All rights reserved. -// http://ceres-solver.org/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Author: sameeragarwal@google.com (Sameer Agarwal) -// -// Template specialization of SchurEliminator. -// -// ======================================== -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -//========================================= -// -// This file is generated using generate_template_specializations.py. -""" - -DYNAMIC_FILE = """ -#include "ceres/schur_eliminator_impl.h" - -namespace ceres { -namespace internal { - -template class SchurEliminator<%s, %s, %s>; - -} // namespace internal -} // namespace ceres -""" - -SPECIALIZATION_FILE = """ -// This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" - -#ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION - -#include "ceres/schur_eliminator_impl.h" - -namespace ceres { -namespace internal { - -template class SchurEliminator<%s, %s, %s>; - -} // namespace internal -} // namespace ceres - -#endif // CERES_RESTRICT_SCHUR_SPECIALIZATION -""" - -FACTORY_FILE_HEADER = """ -#include "ceres/linear_solver.h" -#include "ceres/schur_eliminator.h" - -namespace ceres { -namespace internal { - -SchurEliminatorBase* SchurEliminatorBase::Create( - const LinearSolver::Options& options) { -#ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION -""" - -FACTORY = """ return new SchurEliminator<%s, %s, %s>(options);""" - -FACTORY_FOOTER = """ -#endif - VLOG(1) << "Template specializations not found for <" - << options.row_block_size << "," << options.e_block_size << "," - << options.f_block_size << ">"; - return new SchurEliminator<Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic>( - options); -} - -} // namespace internal -} // namespace ceres -""" diff --git a/extern/ceres/internal/ceres/schur_jacobi_preconditioner.cc b/extern/ceres/internal/ceres/schur_jacobi_preconditioner.cc index 89d770b405a..3ecec728262 100644 --- a/extern/ceres/internal/ceres/schur_jacobi_preconditioner.cc +++ b/extern/ceres/internal/ceres/schur_jacobi_preconditioner.cc @@ -43,26 +43,25 @@ namespace ceres { namespace internal { SchurJacobiPreconditioner::SchurJacobiPreconditioner( - const CompressedRowBlockStructure& bs, - const Preconditioner::Options& options) - : options_(options) { + const CompressedRowBlockStructure& bs, Preconditioner::Options options) + : options_(std::move(options)) { CHECK_GT(options_.elimination_groups.size(), 1); CHECK_GT(options_.elimination_groups[0], 0); const int num_blocks = bs.cols.size() - options_.elimination_groups[0]; CHECK_GT(num_blocks, 0) << "Jacobian should have at least 1 f_block for " << "SCHUR_JACOBI preconditioner."; - CHECK(options_.context != NULL); + CHECK(options_.context != nullptr); std::vector<int> blocks(num_blocks); for (int i = 0; i < num_blocks; ++i) { blocks[i] = bs.cols[i + options_.elimination_groups[0]].size; } - m_.reset(new BlockRandomAccessDiagonalMatrix(blocks)); + m_ = std::make_unique<BlockRandomAccessDiagonalMatrix>(blocks); InitEliminator(bs); } -SchurJacobiPreconditioner::~SchurJacobiPreconditioner() {} +SchurJacobiPreconditioner::~SchurJacobiPreconditioner() = default; // Initialize the SchurEliminator. void SchurJacobiPreconditioner::InitEliminator( @@ -74,7 +73,7 @@ void SchurJacobiPreconditioner::InitEliminator( eliminator_options.f_block_size = options_.f_block_size; eliminator_options.row_block_size = options_.row_block_size; eliminator_options.context = options_.context; - eliminator_.reset(SchurEliminatorBase::Create(eliminator_options)); + eliminator_ = SchurEliminatorBase::Create(eliminator_options); const bool kFullRankETE = true; eliminator_->Init( eliminator_options.elimination_groups[0], kFullRankETE, &bs); diff --git a/extern/ceres/internal/ceres/schur_jacobi_preconditioner.h b/extern/ceres/internal/ceres/schur_jacobi_preconditioner.h index 372b790b82f..a43bc3388a1 100644 --- a/extern/ceres/internal/ceres/schur_jacobi_preconditioner.h +++ b/extern/ceres/internal/ceres/schur_jacobi_preconditioner.h @@ -43,6 +43,8 @@ #include <utility> #include <vector> +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/preconditioner.h" namespace ceres { @@ -69,10 +71,11 @@ class SchurEliminatorBase; // options.elimination_groups.push_back(num_cameras); // SchurJacobiPreconditioner preconditioner( // *A.block_structure(), options); -// preconditioner.Update(A, NULL); +// preconditioner.Update(A, nullptr); // preconditioner.RightMultiply(x, y); // -class SchurJacobiPreconditioner : public BlockSparseMatrixPreconditioner { +class CERES_NO_EXPORT SchurJacobiPreconditioner + : public BlockSparseMatrixPreconditioner { public: // Initialize the symbolic structure of the preconditioner. bs is // the block structure of the linear system to be solved. It is used @@ -81,11 +84,11 @@ class SchurJacobiPreconditioner : public BlockSparseMatrixPreconditioner { // It has the same structural requirement as other Schur complement // based solvers. Please see schur_eliminator.h for more details. SchurJacobiPreconditioner(const CompressedRowBlockStructure& bs, - const Preconditioner::Options& options); + Preconditioner::Options options); SchurJacobiPreconditioner(const SchurJacobiPreconditioner&) = delete; void operator=(const SchurJacobiPreconditioner&) = delete; - virtual ~SchurJacobiPreconditioner(); + ~SchurJacobiPreconditioner() override; // Preconditioner interface. void RightMultiply(const double* x, double* y) const final; @@ -104,4 +107,6 @@ class SchurJacobiPreconditioner : public BlockSparseMatrixPreconditioner { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_SCHUR_JACOBI_PRECONDITIONER_H_ diff --git a/extern/ceres/internal/ceres/schur_templates.h b/extern/ceres/internal/ceres/schur_templates.h index 90aee0a1afc..cacee20c412 100644 --- a/extern/ceres/internal/ceres/schur_templates.h +++ b/extern/ceres/internal/ceres/schur_templates.h @@ -32,11 +32,14 @@ #ifndef CERES_INTERNAL_SCHUR_TEMPLATES_H_ #define CERES_INTERNAL_SCHUR_TEMPLATES_H_ +#include "ceres/internal/config.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { namespace internal { +CERES_NO_EXPORT void GetBestSchurTemplateSpecialization(int* row_block_size, int* e_block_size, int* f_block_size); diff --git a/extern/ceres/internal/ceres/scoped_thread_token.h b/extern/ceres/internal/ceres/scoped_thread_token.h index c167397cce9..533bfd5a387 100644 --- a/extern/ceres/internal/ceres/scoped_thread_token.h +++ b/extern/ceres/internal/ceres/scoped_thread_token.h @@ -31,6 +31,7 @@ #ifndef CERES_INTERNAL_SCOPED_THREAD_TOKEN_H_ #define CERES_INTERNAL_SCOPED_THREAD_TOKEN_H_ +#include "ceres/internal/export.h" #include "ceres/thread_token_provider.h" namespace ceres { @@ -38,21 +39,20 @@ namespace internal { // Helper class for ThreadTokenProvider. This object acquires a token in its // constructor and puts that token back with destruction. -class ScopedThreadToken { +class CERES_NO_EXPORT ScopedThreadToken { public: - ScopedThreadToken(ThreadTokenProvider* provider) + explicit ScopedThreadToken(ThreadTokenProvider* provider) : provider_(provider), token_(provider->Acquire()) {} ~ScopedThreadToken() { provider_->Release(token_); } + ScopedThreadToken(ScopedThreadToken&) = delete; + ScopedThreadToken& operator=(ScopedThreadToken&) = delete; int token() const { return token_; } private: ThreadTokenProvider* provider_; int token_; - - ScopedThreadToken(ScopedThreadToken&); - ScopedThreadToken& operator=(ScopedThreadToken&); }; } // namespace internal diff --git a/extern/ceres/internal/ceres/scratch_evaluate_preparer.cc b/extern/ceres/internal/ceres/scratch_evaluate_preparer.cc index 9905b220fbf..0a1b0f3e7d1 100644 --- a/extern/ceres/internal/ceres/scratch_evaluate_preparer.cc +++ b/extern/ceres/internal/ceres/scratch_evaluate_preparer.cc @@ -30,6 +30,8 @@ #include "ceres/scratch_evaluate_preparer.h" +#include <memory> + #include "ceres/parameter_block.h" #include "ceres/program.h" #include "ceres/residual_block.h" @@ -37,9 +39,9 @@ namespace ceres { namespace internal { -ScratchEvaluatePreparer* ScratchEvaluatePreparer::Create(const Program& program, - int num_threads) { - ScratchEvaluatePreparer* preparers = new ScratchEvaluatePreparer[num_threads]; +std::unique_ptr<ScratchEvaluatePreparer[]> ScratchEvaluatePreparer::Create( + const Program& program, int num_threads) { + auto preparers = std::make_unique<ScratchEvaluatePreparer[]>(num_threads); int max_derivatives_per_residual_block = program.MaxDerivativesPerResidualBlock(); for (int i = 0; i < num_threads; i++) { @@ -49,7 +51,8 @@ ScratchEvaluatePreparer* ScratchEvaluatePreparer::Create(const Program& program, } void ScratchEvaluatePreparer::Init(int max_derivatives_per_residual_block) { - jacobian_scratch_.reset(new double[max_derivatives_per_residual_block]); + jacobian_scratch_ = + std::make_unique<double[]>(max_derivatives_per_residual_block); } // Point the jacobian blocks into the scratch area of this evaluate preparer. @@ -64,10 +67,10 @@ void ScratchEvaluatePreparer::Prepare(const ResidualBlock* residual_block, const ParameterBlock* parameter_block = residual_block->parameter_blocks()[j]; if (parameter_block->IsConstant()) { - jacobians[j] = NULL; + jacobians[j] = nullptr; } else { jacobians[j] = jacobian_block_cursor; - jacobian_block_cursor += num_residuals * parameter_block->LocalSize(); + jacobian_block_cursor += num_residuals * parameter_block->TangentSize(); } } } diff --git a/extern/ceres/internal/ceres/scratch_evaluate_preparer.h b/extern/ceres/internal/ceres/scratch_evaluate_preparer.h index 2d2745d6269..3f4e7df8de0 100644 --- a/extern/ceres/internal/ceres/scratch_evaluate_preparer.h +++ b/extern/ceres/internal/ceres/scratch_evaluate_preparer.h @@ -37,6 +37,9 @@ #include <memory> +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" + namespace ceres { namespace internal { @@ -44,11 +47,11 @@ class Program; class ResidualBlock; class SparseMatrix; -class ScratchEvaluatePreparer { +class CERES_NO_EXPORT ScratchEvaluatePreparer { public: // Create num_threads ScratchEvaluatePreparers. - static ScratchEvaluatePreparer* Create(const Program& program, - int num_threads); + static std::unique_ptr<ScratchEvaluatePreparer[]> Create( + const Program& program, int num_threads); // EvaluatePreparer interface void Init(int max_derivatives_per_residual_block); @@ -66,4 +69,6 @@ class ScratchEvaluatePreparer { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_SCRATCH_EVALUATE_PREPARER_H_ diff --git a/extern/ceres/internal/ceres/single_linkage_clustering.h b/extern/ceres/internal/ceres/single_linkage_clustering.h index e891a9eec0a..b4a7e077619 100644 --- a/extern/ceres/internal/ceres/single_linkage_clustering.h +++ b/extern/ceres/internal/ceres/single_linkage_clustering.h @@ -34,7 +34,8 @@ #include <unordered_map> #include "ceres/graph.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -55,12 +56,14 @@ struct SingleLinkageClusteringOptions { // // The return value of this function is the number of clusters // identified by the algorithm. -int CERES_EXPORT_INTERNAL -ComputeSingleLinkageClustering(const SingleLinkageClusteringOptions& options, - const WeightedGraph<int>& graph, - std::unordered_map<int, int>* membership); +CERES_NO_EXPORT int ComputeSingleLinkageClustering( + const SingleLinkageClusteringOptions& options, + const WeightedGraph<int>& graph, + std::unordered_map<int, int>* membership); } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_SINGLE_LINKAGE_CLUSTERING_H_ diff --git a/extern/ceres/internal/ceres/small_blas.h b/extern/ceres/internal/ceres/small_blas.h index 4ee9229f35f..1cf41a5f1c2 100644 --- a/extern/ceres/internal/ceres/small_blas.h +++ b/extern/ceres/internal/ceres/small_blas.h @@ -36,7 +36,7 @@ #define CERES_INTERNAL_SMALL_BLAS_H_ #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "glog/logging.h" #include "small_blas_generic.h" @@ -210,7 +210,7 @@ CERES_GEMM_BEGIN(MatrixMatrixMultiplyNaive) { // Process the couple columns in remainder if present. if (NUM_COL_C & 2) { - int col = NUM_COL_C & (int)(~(span - 1)); + int col = NUM_COL_C & (~(span - 1)); const double* pa = &A[0]; for (int row = 0; row < NUM_ROW_C; ++row, pa += NUM_COL_A) { const double* pb = &B[col]; @@ -232,7 +232,7 @@ CERES_GEMM_BEGIN(MatrixMatrixMultiplyNaive) { } // Calculate the main part with multiples of 4. - int col_m = NUM_COL_C & (int)(~(span - 1)); + int col_m = NUM_COL_C & (~(span - 1)); for (int col = 0; col < col_m; col += span) { for (int row = 0; row < NUM_ROW_C; ++row) { const int index = (row + start_row_c) * col_stride_c + start_col_c + col; @@ -315,7 +315,7 @@ CERES_GEMM_BEGIN(MatrixTransposeMatrixMultiplyNaive) { // Process the couple columns in remainder if present. if (NUM_COL_C & 2) { - int col = NUM_COL_C & (int)(~(span - 1)); + int col = NUM_COL_C & (~(span - 1)); for (int row = 0; row < NUM_ROW_C; ++row) { const double* pa = &A[row]; const double* pb = &B[col]; @@ -339,7 +339,7 @@ CERES_GEMM_BEGIN(MatrixTransposeMatrixMultiplyNaive) { } // Process the main part with multiples of 4. - int col_m = NUM_COL_C & (int)(~(span - 1)); + int col_m = NUM_COL_C & (~(span - 1)); for (int col = 0; col < col_m; col += span) { for (int row = 0; row < NUM_ROW_C; ++row) { const int index = (row + start_row_c) * col_stride_c + start_col_c + col; @@ -435,7 +435,7 @@ inline void MatrixVectorMultiply(const double* A, // Process the couple rows in remainder if present. if (NUM_ROW_A & 2) { - int row = NUM_ROW_A & (int)(~(span - 1)); + int row = NUM_ROW_A & (~(span - 1)); const double* pa1 = &A[row * NUM_COL_A]; const double* pa2 = pa1 + NUM_COL_A; const double* pb = &b[0]; @@ -454,7 +454,7 @@ inline void MatrixVectorMultiply(const double* A, } // Calculate the main part with multiples of 4. - int row_m = NUM_ROW_A & (int)(~(span - 1)); + int row_m = NUM_ROW_A & (~(span - 1)); for (int row = 0; row < row_m; row += span) { // clang-format off MVM_mat4x1(NUM_COL_A, &A[row * NUM_COL_A], NUM_COL_A, @@ -522,7 +522,7 @@ inline void MatrixTransposeVectorMultiply(const double* A, // Process the couple columns in remainder if present. if (NUM_COL_A & 2) { - int row = NUM_COL_A & (int)(~(span - 1)); + int row = NUM_COL_A & (~(span - 1)); const double* pa = &A[row]; const double* pb = &b[0]; double tmp1 = 0.0, tmp2 = 0.0; @@ -543,7 +543,7 @@ inline void MatrixTransposeVectorMultiply(const double* A, } // Calculate the main part with multiples of 4. - int row_m = NUM_COL_A & (int)(~(span - 1)); + int row_m = NUM_COL_A & (~(span - 1)); for (int row = 0; row < row_m; row += span) { // clang-format off MTV_mat4x1(NUM_ROW_A, &A[row], NUM_COL_A, diff --git a/extern/ceres/internal/ceres/small_blas_generic.h b/extern/ceres/internal/ceres/small_blas_generic.h index 3f3ea424c80..f5aa909a8a3 100644 --- a/extern/ceres/internal/ceres/small_blas_generic.h +++ b/extern/ceres/internal/ceres/small_blas_generic.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2018 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -100,10 +100,11 @@ static inline void MMM_mat1x4(const int col_a, #define CERES_GEMM_OPT_MMM_MAT1X4_MUL \ av = pa[k]; \ pb = b + bi; \ - c0 += av * *pb++; \ - c1 += av * *pb++; \ - c2 += av * *pb++; \ - c3 += av * *pb++; \ + c0 += av * pb[0]; \ + c1 += av * pb[1]; \ + c2 += av * pb[2]; \ + c3 += av * pb[3]; \ + pb += 4; \ bi += col_stride_b; \ k++; @@ -167,10 +168,11 @@ static inline void MTM_mat1x4(const int col_a, #define CERES_GEMM_OPT_MTM_MAT1X4_MUL \ av = pa[ai]; \ pb = b + bi; \ - c0 += av * *pb++; \ - c1 += av * *pb++; \ - c2 += av * *pb++; \ - c3 += av * *pb++; \ + c0 += av * pb[0]; \ + c1 += av * pb[1]; \ + c2 += av * pb[2]; \ + c3 += av * pb[3]; \ + pb += 4; \ ai += col_stride_a; \ bi += col_stride_b; diff --git a/extern/ceres/internal/ceres/solver.cc b/extern/ceres/internal/ceres/solver.cc index dfde1221b61..150c5550fc9 100644 --- a/extern/ceres/internal/ceres/solver.cc +++ b/extern/ceres/internal/ceres/solver.cc @@ -41,7 +41,7 @@ #include "ceres/context_impl.h" #include "ceres/detect_structure.h" #include "ceres/gradient_checking_cost_function.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/parameter_block_ordering.h" #include "ceres/preprocessor.h" #include "ceres/problem.h" @@ -141,16 +141,20 @@ bool TrustRegionOptionsAreValid(const Solver::Options& options, string* error) { return false; } - if (options.dense_linear_algebra_library_type == LAPACK && - !IsDenseLinearAlgebraLibraryTypeAvailable(LAPACK) && + if (!IsDenseLinearAlgebraLibraryTypeAvailable( + options.dense_linear_algebra_library_type) && (options.linear_solver_type == DENSE_NORMAL_CHOLESKY || options.linear_solver_type == DENSE_QR || options.linear_solver_type == DENSE_SCHUR)) { *error = StringPrintf( "Can't use %s with " - "Solver::Options::dense_linear_algebra_library_type = LAPACK " - "because LAPACK was not enabled when Ceres was built.", - LinearSolverTypeToString(options.linear_solver_type)); + "Solver::Options::dense_linear_algebra_library_type = %s " + "because %s was not enabled when Ceres was built.", + LinearSolverTypeToString(options.linear_solver_type), + DenseLinearAlgebraLibraryTypeToString( + options.dense_linear_algebra_library_type), + DenseLinearAlgebraLibraryTypeToString( + options.dense_linear_algebra_library_type)); return false; } @@ -367,7 +371,7 @@ void PostSolveSummarize(const internal::PreprocessedProblem& pp, &(summary->inner_iteration_ordering_used)); // clang-format off - summary->inner_iterations_used = pp.inner_iteration_minimizer.get() != NULL; // NOLINT + summary->inner_iterations_used = pp.inner_iteration_minimizer.get() != nullptr; // NOLINT summary->linear_solver_type_used = pp.linear_solver_options.type; summary->num_threads_used = pp.options.num_threads; summary->preconditioner_type_used = pp.options.preconditioner_type; @@ -375,7 +379,7 @@ void PostSolveSummarize(const internal::PreprocessedProblem& pp, internal::SetSummaryFinalCost(summary); - if (pp.reduced_program.get() != NULL) { + if (pp.reduced_program.get() != nullptr) { SummarizeReducedProgram(*pp.reduced_program, summary); } @@ -385,7 +389,7 @@ void PostSolveSummarize(const internal::PreprocessedProblem& pp, // case if the preprocessor failed, or if the reduced problem did // not contain any parameter blocks. Thus, only extract the // evaluator statistics if one exists. - if (pp.evaluator.get() != NULL) { + if (pp.evaluator.get() != nullptr) { const map<string, CallStatistics>& evaluator_statistics = pp.evaluator->Statistics(); { @@ -407,7 +411,7 @@ void PostSolveSummarize(const internal::PreprocessedProblem& pp, // Again, like the evaluator, there may or may not be a linear // solver from which we can extract run time statistics. In // particular the line search solver does not use a linear solver. - if (pp.linear_solver.get() != NULL) { + if (pp.linear_solver.get() != nullptr) { const map<string, CallStatistics>& linear_solver_statistics = pp.linear_solver->Statistics(); const CallStatistics& call_stats = FindWithDefault( @@ -436,8 +440,7 @@ void Minimize(internal::PreprocessedProblem* pp, Solver::Summary* summary) { } const Vector original_reduced_parameters = pp->reduced_parameters; - std::unique_ptr<Minimizer> minimizer( - Minimizer::Create(pp->options.minimizer_type)); + auto minimizer = Minimizer::Create(pp->options.minimizer_type); minimizer->Minimize( pp->minimizer_options, pp->reduced_parameters.data(), summary); @@ -485,7 +488,7 @@ bool Solver::Options::IsValid(string* error) const { return LineSearchOptionsAreValid(*this, error); } -Solver::~Solver() {} +Solver::~Solver() = default; void Solver::Solve(const Solver::Options& options, Problem* problem, @@ -518,11 +521,11 @@ void Solver::Solve(const Solver::Options& options, Solver::Options modified_options = options; if (options.check_gradients) { modified_options.callbacks.push_back(&gradient_checking_callback); - gradient_checking_problem.reset(CreateGradientCheckingProblemImpl( + gradient_checking_problem = CreateGradientCheckingProblemImpl( problem_impl, options.gradient_check_numeric_derivative_relative_step_size, options.gradient_check_relative_precision, - &gradient_checking_callback)); + &gradient_checking_callback); problem_impl = gradient_checking_problem.get(); program = problem_impl->mutable_program(); } @@ -534,8 +537,7 @@ void Solver::Solve(const Solver::Options& options, // The main thread also does work so we only need to launch num_threads - 1. problem_impl->context()->EnsureMinimumThreads(options.num_threads - 1); - std::unique_ptr<Preprocessor> preprocessor( - Preprocessor::Create(modified_options.minimizer_type)); + auto preprocessor = Preprocessor::Create(modified_options.minimizer_type); PreprocessedProblem pp; const bool status = diff --git a/extern/ceres/internal/ceres/solver_utils.cc b/extern/ceres/internal/ceres/solver_utils.cc index eb5aafa061c..22fa137055d 100644 --- a/extern/ceres/internal/ceres/solver_utils.cc +++ b/extern/ceres/internal/ceres/solver_utils.cc @@ -34,8 +34,11 @@ #include "Eigen/Core" #include "ceres/internal/config.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/version.h" +#ifndef CERES_NO_CUDA +#include "cuda_runtime.h" +#endif // CERES_NO_CUDA namespace ceres { namespace internal { @@ -87,6 +90,10 @@ std::string VersionString() { value += "-no_custom_blas"; #endif +#ifndef CERES_NO_CUDA + value += "-cuda-(" + std::to_string(CUDART_VERSION) + ")"; +#endif + return value; } diff --git a/extern/ceres/internal/ceres/solver_utils.h b/extern/ceres/internal/ceres/solver_utils.h index 85fbf3776ab..298564a897d 100644 --- a/extern/ceres/internal/ceres/solver_utils.h +++ b/extern/ceres/internal/ceres/solver_utils.h @@ -28,9 +28,14 @@ // // Author: sameeragarwal@google.com (Sameer Agarwal) +#ifndef CERES_INTERNAL_SOLVER_UTILS_H_ +#define CERES_INTERNAL_SOLVER_UTILS_H_ + #include <algorithm> #include <string> +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/iteration_callback.h" #include "ceres/types.h" @@ -55,7 +60,12 @@ void SetSummaryFinalCost(SummaryType* summary) { } } +CERES_NO_EXPORT std::string VersionString(); } // namespace internal } // namespace ceres + +#include "ceres/internal/reenable_warnings.h" + +#endif // CERES_INTERNAL_SOLVER_UTILS_H_ diff --git a/extern/ceres/internal/ceres/sparse_cholesky.cc b/extern/ceres/internal/ceres/sparse_cholesky.cc index 91cdf671b1a..4a80470ffb7 100644 --- a/extern/ceres/internal/ceres/sparse_cholesky.cc +++ b/extern/ceres/internal/ceres/sparse_cholesky.cc @@ -30,6 +30,8 @@ #include "ceres/sparse_cholesky.h" +#include <memory> + #include "ceres/accelerate_sparse.h" #include "ceres/cxsparse.h" #include "ceres/eigensparse.h" @@ -113,7 +115,7 @@ std::unique_ptr<SparseCholesky> SparseCholesky::Create( return sparse_cholesky; } -SparseCholesky::~SparseCholesky() {} +SparseCholesky::~SparseCholesky() = default; LinearSolverTerminationType SparseCholesky::FactorAndSolve( CompressedRowSparseMatrix* lhs, @@ -133,7 +135,7 @@ RefinedSparseCholesky::RefinedSparseCholesky( : sparse_cholesky_(std::move(sparse_cholesky)), iterative_refiner_(std::move(iterative_refiner)) {} -RefinedSparseCholesky::~RefinedSparseCholesky() {} +RefinedSparseCholesky::~RefinedSparseCholesky() = default; CompressedRowSparseMatrix::StorageType RefinedSparseCholesky::StorageType() const { diff --git a/extern/ceres/internal/ceres/sparse_cholesky.h b/extern/ceres/internal/ceres/sparse_cholesky.h index a6af6b2c207..80c5cb2b83b 100644 --- a/extern/ceres/internal/ceres/sparse_cholesky.h +++ b/extern/ceres/internal/ceres/sparse_cholesky.h @@ -33,11 +33,13 @@ // This include must come before any #ifndef check on Ceres compile options. // clang-format off -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" // clang-format on #include <memory> +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" #include "glog/logging.h" @@ -67,7 +69,7 @@ namespace internal { // CHECK_EQ(sparse_cholesky->Solve(rhs.data(), solution.data(), &message), // LINEAR_SOLVER_SUCCESS); -class CERES_EXPORT_INTERNAL SparseCholesky { +class CERES_NO_EXPORT SparseCholesky { public: static std::unique_ptr<SparseCholesky> Create( const LinearSolver::Options& options); @@ -104,29 +106,28 @@ class CERES_EXPORT_INTERNAL SparseCholesky { // Convenience method which combines a call to Factorize and // Solve. Solve is only called if Factorize returns // LINEAR_SOLVER_SUCCESS. - virtual LinearSolverTerminationType FactorAndSolve( - CompressedRowSparseMatrix* lhs, - const double* rhs, - double* solution, - std::string* message); + LinearSolverTerminationType FactorAndSolve(CompressedRowSparseMatrix* lhs, + const double* rhs, + double* solution, + std::string* message); }; class IterativeRefiner; // Computes an initial solution using the given instance of // SparseCholesky, and then refines it using the IterativeRefiner. -class CERES_EXPORT_INTERNAL RefinedSparseCholesky : public SparseCholesky { +class CERES_NO_EXPORT RefinedSparseCholesky final : public SparseCholesky { public: RefinedSparseCholesky(std::unique_ptr<SparseCholesky> sparse_cholesky, std::unique_ptr<IterativeRefiner> iterative_refiner); - virtual ~RefinedSparseCholesky(); + ~RefinedSparseCholesky() override; - virtual CompressedRowSparseMatrix::StorageType StorageType() const; - virtual LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, - std::string* message); - virtual LinearSolverTerminationType Solve(const double* rhs, - double* solution, - std::string* message); + CompressedRowSparseMatrix::StorageType StorageType() const override; + LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, + std::string* message) override; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override; private: std::unique_ptr<SparseCholesky> sparse_cholesky_; @@ -137,4 +138,6 @@ class CERES_EXPORT_INTERNAL RefinedSparseCholesky : public SparseCholesky { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_SPARSE_CHOLESKY_H_ diff --git a/extern/ceres/internal/ceres/sparse_matrix.cc b/extern/ceres/internal/ceres/sparse_matrix.cc index 32388f58fc3..bc757ead361 100644 --- a/extern/ceres/internal/ceres/sparse_matrix.cc +++ b/extern/ceres/internal/ceres/sparse_matrix.cc @@ -33,7 +33,7 @@ namespace ceres { namespace internal { -SparseMatrix::~SparseMatrix() {} +SparseMatrix::~SparseMatrix() = default; } // namespace internal } // namespace ceres diff --git a/extern/ceres/internal/ceres/sparse_matrix.h b/extern/ceres/internal/ceres/sparse_matrix.h index b57f10890fc..1dbb96e6070 100644 --- a/extern/ceres/internal/ceres/sparse_matrix.h +++ b/extern/ceres/internal/ceres/sparse_matrix.h @@ -36,7 +36,7 @@ #include <cstdio> #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/linear_operator.h" #include "ceres/types.h" @@ -64,14 +64,14 @@ namespace internal { // matrix type dependent and we are at this stage unable to come up // with an efficient high level interface that spans multiple sparse // matrix types. -class CERES_EXPORT_INTERNAL SparseMatrix : public LinearOperator { +class CERES_NO_EXPORT SparseMatrix : public LinearOperator { public: - virtual ~SparseMatrix(); + ~SparseMatrix() override; // y += Ax; - virtual void RightMultiply(const double* x, double* y) const = 0; + void RightMultiply(const double* x, double* y) const override = 0; // y += A'x; - virtual void LeftMultiply(const double* x, double* y) const = 0; + void LeftMultiply(const double* x, double* y) const override = 0; // In MATLAB notation sum(A.*A, 1) virtual void SquaredColumnNorm(double* x) const = 0; @@ -98,8 +98,8 @@ class CERES_EXPORT_INTERNAL SparseMatrix : public LinearOperator { virtual double* mutable_values() = 0; virtual const double* values() const = 0; - virtual int num_rows() const = 0; - virtual int num_cols() const = 0; + int num_rows() const override = 0; + int num_cols() const override = 0; virtual int num_nonzeros() const = 0; }; diff --git a/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc b/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc index 0f2e589d041..2e52ae6d908 100644 --- a/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc +++ b/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc @@ -54,7 +54,7 @@ SparseNormalCholeskySolver::SparseNormalCholeskySolver( sparse_cholesky_ = SparseCholesky::Create(options); } -SparseNormalCholeskySolver::~SparseNormalCholeskySolver() {} +SparseNormalCholeskySolver::~SparseNormalCholeskySolver() = default; LinearSolver::Summary SparseNormalCholeskySolver::SolveImpl( BlockSparseMatrix* A, @@ -75,21 +75,21 @@ LinearSolver::Summary SparseNormalCholeskySolver::SolveImpl( A->LeftMultiply(b, rhs_.data()); event_logger.AddEvent("Compute RHS"); - if (per_solve_options.D != NULL) { + if (per_solve_options.D != nullptr) { // Temporarily append a diagonal block to the A matrix, but undo // it before returning the matrix to the user. - std::unique_ptr<BlockSparseMatrix> regularizer; - regularizer.reset(BlockSparseMatrix::CreateDiagonalMatrix( - per_solve_options.D, A->block_structure()->cols)); + std::unique_ptr<BlockSparseMatrix> regularizer = + BlockSparseMatrix::CreateDiagonalMatrix(per_solve_options.D, + A->block_structure()->cols); event_logger.AddEvent("Diagonal"); A->AppendRows(*regularizer); event_logger.AddEvent("Append"); } event_logger.AddEvent("Append Rows"); - if (inner_product_computer_.get() == NULL) { - inner_product_computer_.reset( - InnerProductComputer::Create(*A, sparse_cholesky_->StorageType())); + if (inner_product_computer_.get() == nullptr) { + inner_product_computer_ = + InnerProductComputer::Create(*A, sparse_cholesky_->StorageType()); event_logger.AddEvent("InnerProductComputer::Create"); } @@ -97,7 +97,7 @@ LinearSolver::Summary SparseNormalCholeskySolver::SolveImpl( inner_product_computer_->Compute(); event_logger.AddEvent("InnerProductComputer::Compute"); - if (per_solve_options.D != NULL) { + if (per_solve_options.D != nullptr) { A->DeleteRowBlocks(A->block_structure()->cols.size()); } diff --git a/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.h b/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.h index ef3274323f5..caec566612e 100644 --- a/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.h +++ b/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.h @@ -36,11 +36,13 @@ // This include must come before any #ifndef check on Ceres compile options. // clang-format off -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" // clang-format on +#include <memory> #include <vector> +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { @@ -52,13 +54,14 @@ class SparseCholesky; // Solves the normal equations (A'A + D'D) x = A'b, using the sparse // linear algebra library of the user's choice. -class SparseNormalCholeskySolver : public BlockSparseMatrixSolver { +class CERES_NO_EXPORT SparseNormalCholeskySolver + : public BlockSparseMatrixSolver { public: explicit SparseNormalCholeskySolver(const LinearSolver::Options& options); SparseNormalCholeskySolver(const SparseNormalCholeskySolver&) = delete; void operator=(const SparseNormalCholeskySolver&) = delete; - virtual ~SparseNormalCholeskySolver(); + ~SparseNormalCholeskySolver() override; private: LinearSolver::Summary SolveImpl(BlockSparseMatrix* A, diff --git a/extern/ceres/internal/ceres/split.cc b/extern/ceres/internal/ceres/split.cc deleted file mode 100644 index 804f4412deb..00000000000 --- a/extern/ceres/internal/ceres/split.cc +++ /dev/null @@ -1,122 +0,0 @@ -// Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. -// http://ceres-solver.org/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Author: keir@google.com (Keir Mierle) - -#include "ceres/split.h" - -#include <iterator> -#include <string> -#include <vector> - -#include "ceres/internal/port.h" - -namespace ceres { -namespace internal { - -using std::string; -using std::vector; - -// If we know how much to allocate for a vector of strings, we can allocate the -// vector<string> only once and directly to the right size. This saves in -// between 33-66 % of memory space needed for the result, and runs faster in the -// microbenchmarks. -// -// The reserve is only implemented for the single character delim. -// -// The implementation for counting is cut-and-pasted from -// SplitStringToIteratorUsing. I could have written my own counting iterator, -// and use the existing template function, but probably this is more clear and -// more sure to get optimized to reasonable code. -static int CalculateReserveForVector(const string& full, const char* delim) { - int count = 0; - if (delim[0] != '\0' && delim[1] == '\0') { - // Optimize the common case where delim is a single character. - char c = delim[0]; - const char* p = full.data(); - const char* end = p + full.size(); - while (p != end) { - if (*p == c) { // This could be optimized with hasless(v,1) trick. - ++p; - } else { - while (++p != end && *p != c) { - // Skip to the next occurence of the delimiter. - } - ++count; - } - } - } - return count; -} - -template <typename StringType, typename ITR> -static inline void SplitStringToIteratorUsing(const StringType& full, - const char* delim, - ITR& result) { - // Optimize the common case where delim is a single character. - if (delim[0] != '\0' && delim[1] == '\0') { - char c = delim[0]; - const char* p = full.data(); - const char* end = p + full.size(); - while (p != end) { - if (*p == c) { - ++p; - } else { - const char* start = p; - while (++p != end && *p != c) { - // Skip to the next occurence of the delimiter. - } - *result++ = StringType(start, p - start); - } - } - return; - } - - string::size_type begin_index, end_index; - begin_index = full.find_first_not_of(delim); - while (begin_index != string::npos) { - end_index = full.find_first_of(delim, begin_index); - if (end_index == string::npos) { - *result++ = full.substr(begin_index); - return; - } - *result++ = full.substr(begin_index, (end_index - begin_index)); - begin_index = full.find_first_not_of(delim, end_index); - } -} - -void SplitStringUsing(const string& full, - const char* delim, - vector<string>* result) { - result->reserve(result->size() + CalculateReserveForVector(full, delim)); - std::back_insert_iterator<vector<string>> it(*result); - SplitStringToIteratorUsing(full, delim, it); -} - -} // namespace internal -} // namespace ceres diff --git a/extern/ceres/internal/ceres/stl_util.h b/extern/ceres/internal/ceres/stl_util.h index d3411b73376..2af2518f837 100644 --- a/extern/ceres/internal/ceres/stl_util.h +++ b/extern/ceres/internal/ceres/stl_util.h @@ -73,7 +73,7 @@ void STLDeleteUniqueContainerPointers(ForwardIterator begin, // hash_set, or any other STL container which defines sensible begin(), end(), // and clear() methods. // -// If container is NULL, this function is a no-op. +// If container is nullptr, this function is a no-op. // // As an alternative to calling STLDeleteElements() directly, consider // ElementDeleter (defined below), which ensures that your container's elements diff --git a/extern/ceres/internal/ceres/stringprintf.cc b/extern/ceres/internal/ceres/stringprintf.cc index b0e2acce8f9..e45b4301eef 100644 --- a/extern/ceres/internal/ceres/stringprintf.cc +++ b/extern/ceres/internal/ceres/stringprintf.cc @@ -36,7 +36,7 @@ #include <string> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -66,7 +66,7 @@ void StringAppendV(string* dst, const char* format, va_list ap) { // Error or MSVC running out of space. MSVC 8.0 and higher // can be asked about space needed with the special idiom below: va_copy(backup_ap, ap); - result = vsnprintf(NULL, 0, format, backup_ap); + result = vsnprintf(nullptr, 0, format, backup_ap); va_end(backup_ap); #endif diff --git a/extern/ceres/internal/ceres/stringprintf.h b/extern/ceres/internal/ceres/stringprintf.h index 4d512784905..e24325fbd35 100644 --- a/extern/ceres/internal/ceres/stringprintf.h +++ b/extern/ceres/internal/ceres/stringprintf.h @@ -41,7 +41,8 @@ #include <cstdarg> #include <string> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -63,32 +64,35 @@ namespace internal { #endif // Return a C++ string. -CERES_EXPORT_INTERNAL extern std::string StringPrintf(const char* format, ...) +CERES_NO_EXPORT extern std::string StringPrintf(const char* format, ...) // Tell the compiler to do printf format string checking. CERES_PRINTF_ATTRIBUTE(1, 2); // Store result into a supplied string and return it. -CERES_EXPORT_INTERNAL extern const std::string& SStringPrintf( - std::string* dst, const char* format, ...) +CERES_NO_EXPORT extern const std::string& SStringPrintf(std::string* dst, + const char* format, + ...) // Tell the compiler to do printf format string checking. CERES_PRINTF_ATTRIBUTE(2, 3); // Append result to a supplied string. -CERES_EXPORT_INTERNAL extern void StringAppendF(std::string* dst, - const char* format, - ...) +CERES_NO_EXPORT extern void StringAppendF(std::string* dst, + const char* format, + ...) // Tell the compiler to do printf format string checking. CERES_PRINTF_ATTRIBUTE(2, 3); // Lower-level routine that takes a va_list and appends to a specified string. // All other routines are just convenience wrappers around it. -CERES_EXPORT_INTERNAL extern void StringAppendV(std::string* dst, - const char* format, - va_list ap); +CERES_NO_EXPORT extern void StringAppendV(std::string* dst, + const char* format, + va_list ap); #undef CERES_PRINTF_ATTRIBUTE } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_STRINGPRINTF_H_ diff --git a/extern/ceres/internal/ceres/subset_preconditioner.cc b/extern/ceres/internal/ceres/subset_preconditioner.cc index 779a34ae741..221530c0dd5 100644 --- a/extern/ceres/internal/ceres/subset_preconditioner.cc +++ b/extern/ceres/internal/ceres/subset_preconditioner.cc @@ -32,6 +32,7 @@ #include <memory> #include <string> +#include <utility> #include "ceres/compressed_row_sparse_matrix.h" #include "ceres/inner_product_computer.h" @@ -42,9 +43,9 @@ namespace ceres { namespace internal { -SubsetPreconditioner::SubsetPreconditioner( - const Preconditioner::Options& options, const BlockSparseMatrix& A) - : options_(options), num_cols_(A.num_cols()) { +SubsetPreconditioner::SubsetPreconditioner(Preconditioner::Options options, + const BlockSparseMatrix& A) + : options_(std::move(options)), num_cols_(A.num_cols()) { CHECK_GE(options_.subset_preconditioner_start_row_block, 0) << "Congratulations, you found a bug in Ceres. Please report it."; @@ -55,7 +56,7 @@ SubsetPreconditioner::SubsetPreconditioner( sparse_cholesky_ = SparseCholesky::Create(sparse_cholesky_options); } -SubsetPreconditioner::~SubsetPreconditioner() {} +SubsetPreconditioner::~SubsetPreconditioner() = default; void SubsetPreconditioner::RightMultiply(const double* x, double* y) const { CHECK(x != nullptr); @@ -66,14 +67,14 @@ void SubsetPreconditioner::RightMultiply(const double* x, double* y) const { bool SubsetPreconditioner::UpdateImpl(const BlockSparseMatrix& A, const double* D) { - BlockSparseMatrix* m = const_cast<BlockSparseMatrix*>(&A); + auto* m = const_cast<BlockSparseMatrix*>(&A); const CompressedRowBlockStructure* bs = m->block_structure(); // A = [P] // [Q] // Now add D to A if needed. - if (D != NULL) { + if (D != nullptr) { // A = [P] // [Q] // [D] @@ -82,19 +83,19 @@ bool SubsetPreconditioner::UpdateImpl(const BlockSparseMatrix& A, m->AppendRows(*regularizer); } - if (inner_product_computer_.get() == NULL) { - inner_product_computer_.reset(InnerProductComputer::Create( + if (inner_product_computer_ == nullptr) { + inner_product_computer_ = InnerProductComputer::Create( *m, options_.subset_preconditioner_start_row_block, bs->rows.size(), - sparse_cholesky_->StorageType())); + sparse_cholesky_->StorageType()); } // Compute inner_product = [Q'*Q + D'*D] inner_product_computer_->Compute(); // Unappend D if needed. - if (D != NULL) { + if (D != nullptr) { // A = [P] // [Q] m->DeleteRowBlocks(bs->cols.size()); diff --git a/extern/ceres/internal/ceres/subset_preconditioner.h b/extern/ceres/internal/ceres/subset_preconditioner.h index 9844a669f45..6d07995a136 100644 --- a/extern/ceres/internal/ceres/subset_preconditioner.h +++ b/extern/ceres/internal/ceres/subset_preconditioner.h @@ -33,7 +33,8 @@ #include <memory> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/preconditioner.h" namespace ceres { @@ -67,12 +68,12 @@ class InnerProductComputer; // computationally expensive this preconditioner will be. // // See the tests for example usage. -class CERES_EXPORT_INTERNAL SubsetPreconditioner +class CERES_NO_EXPORT SubsetPreconditioner : public BlockSparseMatrixPreconditioner { public: - SubsetPreconditioner(const Preconditioner::Options& options, + SubsetPreconditioner(Preconditioner::Options options, const BlockSparseMatrix& A); - virtual ~SubsetPreconditioner(); + ~SubsetPreconditioner() override; // Preconditioner interface void RightMultiply(const double* x, double* y) const final; @@ -91,4 +92,6 @@ class CERES_EXPORT_INTERNAL SubsetPreconditioner } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_SUBSET_PRECONDITIONER_H_ diff --git a/extern/ceres/internal/ceres/suitesparse.cc b/extern/ceres/internal/ceres/suitesparse.cc index 0d6f6bdfb88..883dcc8f63e 100644 --- a/extern/ceres/internal/ceres/suitesparse.cc +++ b/extern/ceres/internal/ceres/suitesparse.cc @@ -29,9 +29,10 @@ // Author: sameeragarwal@google.com (Sameer Agarwal) // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_NO_SUITESPARSE +#include <memory> #include <vector> #include "ceres/compressed_col_sparse_matrix_utils.h" @@ -368,7 +369,7 @@ SuiteSparseCholesky::~SuiteSparseCholesky() { LinearSolverTerminationType SuiteSparseCholesky::Factorize( CompressedRowSparseMatrix* lhs, string* message) { if (lhs == nullptr) { - *message = "Failure: Input lhs is NULL."; + *message = "Failure: Input lhs is nullptr."; return LINEAR_SOLVER_FATAL_ERROR; } diff --git a/extern/ceres/internal/ceres/suitesparse.h b/extern/ceres/internal/ceres/suitesparse.h index 5dcc53f0167..3f62e7c7b7d 100644 --- a/extern/ceres/internal/ceres/suitesparse.h +++ b/extern/ceres/internal/ceres/suitesparse.h @@ -34,11 +34,12 @@ #define CERES_INTERNAL_SUITESPARSE_H_ // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_NO_SUITESPARSE #include <cstring> +#include <memory> #include <string> #include <vector> @@ -70,6 +71,8 @@ #define SuiteSparse_long UF_long #endif +#include "ceres/internal/disable_warnings.h" + namespace ceres { namespace internal { @@ -81,7 +84,7 @@ class TripletSparseMatrix; // provides the user with a simpler interface. The methods here cannot // be static as a cholmod_common object serves as a global variable // for all cholmod function calls. -class SuiteSparse { +class CERES_NO_EXPORT SuiteSparse { public: SuiteSparse(); ~SuiteSparse(); @@ -106,7 +109,7 @@ class SuiteSparse { cholmod_dense CreateDenseVectorView(const double* x, int size); // Given a vector x, build a cholmod_dense vector of size out_size - // with the first in_size entries copied from x. If x is NULL, then + // with the first in_size entries copied from x. If x is nullptr, then // an all zeros vector is returned. Caller owns the result. cholmod_dense* CreateDenseVector(const double* x, int in_size, int out_size); @@ -123,7 +126,7 @@ class SuiteSparse { // Create and return a matrix m = A * A'. Caller owns the // result. The matrix A is not modified. cholmod_sparse* AATranspose(cholmod_sparse* A) { - cholmod_sparse* m = cholmod_aat(A, NULL, A->nrow, 1, &cc_); + cholmod_sparse* m = cholmod_aat(A, nullptr, A->nrow, 1, &cc_); m->stype = 1; // Pay attention to the upper triangular part. return m; } @@ -196,7 +199,7 @@ class SuiteSparse { // Given a Cholesky factorization of a matrix A = LL^T, solve the // linear system Ax = b, and return the result. If the Solve fails - // NULL is returned. Caller owns the result. + // nullptr is returned. Caller owns the result. // // message contains an explanation of the failures if any. cholmod_dense* Solve(cholmod_factor* L, @@ -288,12 +291,12 @@ class SuiteSparse { cholmod_common cc_; }; -class SuiteSparseCholesky : public SparseCholesky { +class CERES_NO_EXPORT SuiteSparseCholesky final : public SparseCholesky { public: static std::unique_ptr<SparseCholesky> Create(OrderingType ordering_type); // SparseCholesky interface. - virtual ~SuiteSparseCholesky(); + ~SuiteSparseCholesky() override; CompressedRowSparseMatrix::StorageType StorageType() const final; LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, std::string* message) final; @@ -302,7 +305,7 @@ class SuiteSparseCholesky : public SparseCholesky { std::string* message) final; private: - SuiteSparseCholesky(const OrderingType ordering_type); + explicit SuiteSparseCholesky(const OrderingType ordering_type); const OrderingType ordering_type_; SuiteSparse ss_; @@ -312,14 +315,18 @@ class SuiteSparseCholesky : public SparseCholesky { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #else // CERES_NO_SUITESPARSE typedef void cholmod_factor; +#include "ceres/internal/disable_warnings.h" + namespace ceres { namespace internal { -class SuiteSparse { +class CERES_NO_EXPORT SuiteSparse { public: // Defining this static function even when SuiteSparse is not // available, allows client code to check for the presence of CAMD @@ -332,12 +339,14 @@ class SuiteSparse { return false; } - void Free(void* arg) {} + void Free(void* /*arg*/) {} }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_NO_SUITESPARSE #endif // CERES_INTERNAL_SUITESPARSE_H_ diff --git a/extern/ceres/internal/ceres/thread_pool.cc b/extern/ceres/internal/ceres/thread_pool.cc index 821431cedb4..57f01af5476 100644 --- a/extern/ceres/internal/ceres/thread_pool.cc +++ b/extern/ceres/internal/ceres/thread_pool.cc @@ -29,7 +29,7 @@ // Author: vitus@google.com (Michael Vitus) // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifdef CERES_USE_CXX_THREADS @@ -57,7 +57,7 @@ int ThreadPool::MaxNumThreadsAvailable() { : num_hardware_threads; } -ThreadPool::ThreadPool() {} +ThreadPool::ThreadPool() = default; ThreadPool::ThreadPool(int num_threads) { Resize(num_threads); } @@ -83,7 +83,7 @@ void ThreadPool::Resize(int num_threads) { GetNumAllowedThreads(num_threads) - num_current_threads; for (int i = 0; i < create_num_threads; ++i) { - thread_pool_.push_back(std::thread(&ThreadPool::ThreadMainLoop, this)); + thread_pool_.emplace_back(&ThreadPool::ThreadMainLoop, this); } } diff --git a/extern/ceres/internal/ceres/thread_pool.h b/extern/ceres/internal/ceres/thread_pool.h index cdf6625e196..94ab1e66bd4 100644 --- a/extern/ceres/internal/ceres/thread_pool.h +++ b/extern/ceres/internal/ceres/thread_pool.h @@ -37,7 +37,7 @@ #include <vector> #include "ceres/concurrent_queue.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -58,7 +58,7 @@ namespace internal { // workers to stop. The workers will finish all of the tasks that have already // been added to the thread pool. // -class CERES_EXPORT_INTERNAL ThreadPool { +class CERES_NO_EXPORT ThreadPool { public: // Returns the maximum number of hardware threads. static int MaxNumThreadsAvailable(); diff --git a/extern/ceres/internal/ceres/thread_token_provider.h b/extern/ceres/internal/ceres/thread_token_provider.h index 06dc0438572..918c687eb24 100644 --- a/extern/ceres/internal/ceres/thread_token_provider.h +++ b/extern/ceres/internal/ceres/thread_token_provider.h @@ -32,7 +32,7 @@ #define CERES_INTERNAL_THREAD_TOKEN_PROVIDER_H_ #include "ceres/internal/config.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #ifdef CERES_USE_CXX_THREADS #include "ceres/concurrent_queue.h" @@ -66,9 +66,9 @@ namespace internal { // ttp.Release(token); // return token to the pool // } // -class ThreadTokenProvider { +class CERES_NO_EXPORT ThreadTokenProvider { public: - ThreadTokenProvider(int num_threads); + explicit ThreadTokenProvider(int num_threads); // Returns the first token from the queue. The acquired value must be // given back by Release(). @@ -87,8 +87,8 @@ class ThreadTokenProvider { ConcurrentQueue<int> pool_; #endif - ThreadTokenProvider(ThreadTokenProvider&); - ThreadTokenProvider& operator=(ThreadTokenProvider&); + ThreadTokenProvider(ThreadTokenProvider&) = delete; + ThreadTokenProvider& operator=(ThreadTokenProvider&) = delete; }; } // namespace internal diff --git a/extern/ceres/internal/ceres/triplet_sparse_matrix.cc b/extern/ceres/internal/ceres/triplet_sparse_matrix.cc index 5dbf0e7cd3a..bbb5f676a5d 100644 --- a/extern/ceres/internal/ceres/triplet_sparse_matrix.cc +++ b/extern/ceres/internal/ceres/triplet_sparse_matrix.cc @@ -31,10 +31,10 @@ #include "ceres/triplet_sparse_matrix.h" #include <algorithm> -#include <cstddef> +#include <memory> #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/random.h" #include "ceres/types.h" #include "glog/logging.h" @@ -45,7 +45,7 @@ namespace internal { TripletSparseMatrix::TripletSparseMatrix() : num_rows_(0), num_cols_(0), max_num_nonzeros_(0), num_nonzeros_(0) {} -TripletSparseMatrix::~TripletSparseMatrix() {} +TripletSparseMatrix::~TripletSparseMatrix() = default; TripletSparseMatrix::TripletSparseMatrix(int num_rows, int num_cols, @@ -109,8 +109,9 @@ bool TripletSparseMatrix::AllTripletsWithinBounds() const { for (int i = 0; i < num_nonzeros_; ++i) { // clang-format off if ((rows_[i] < 0) || (rows_[i] >= num_rows_) || - (cols_[i] < 0) || (cols_[i] >= num_cols_)) + (cols_[i] < 0) || (cols_[i] >= num_cols_)) { return false; + } // clang-format on } return true; @@ -123,9 +124,12 @@ void TripletSparseMatrix::Reserve(int new_max_num_nonzeros) { // Nothing to do if we have enough space already. if (new_max_num_nonzeros <= max_num_nonzeros_) return; - int* new_rows = new int[new_max_num_nonzeros]; - int* new_cols = new int[new_max_num_nonzeros]; - double* new_values = new double[new_max_num_nonzeros]; + std::unique_ptr<int[]> new_rows = + std::make_unique<int[]>(new_max_num_nonzeros); + std::unique_ptr<int[]> new_cols = + std::make_unique<int[]>(new_max_num_nonzeros); + std::unique_ptr<double[]> new_values = + std::make_unique<double[]>(new_max_num_nonzeros); for (int i = 0; i < num_nonzeros_; ++i) { new_rows[i] = rows_[i]; @@ -133,10 +137,9 @@ void TripletSparseMatrix::Reserve(int new_max_num_nonzeros) { new_values[i] = values_[i]; } - rows_.reset(new_rows); - cols_.reset(new_cols); - values_.reset(new_values); - + rows_ = std::move(new_rows); + cols_ = std::move(new_cols); + values_ = std::move(new_values); max_num_nonzeros_ = new_max_num_nonzeros; } @@ -152,9 +155,9 @@ void TripletSparseMatrix::set_num_nonzeros(int num_nonzeros) { } void TripletSparseMatrix::AllocateMemory() { - rows_.reset(new int[max_num_nonzeros_]); - cols_.reset(new int[max_num_nonzeros_]); - values_.reset(new double[max_num_nonzeros_]); + rows_ = std::make_unique<int[]>(max_num_nonzeros_); + cols_ = std::make_unique<int[]>(max_num_nonzeros_); + values_ = std::make_unique<double[]>(max_num_nonzeros_); } void TripletSparseMatrix::CopyData(const TripletSparseMatrix& orig) { @@ -252,10 +255,11 @@ void TripletSparseMatrix::Resize(int new_num_rows, int new_num_cols) { num_nonzeros_ -= dropped_terms; } -TripletSparseMatrix* TripletSparseMatrix::CreateSparseDiagonalMatrix( - const double* values, int num_rows) { - TripletSparseMatrix* m = - new TripletSparseMatrix(num_rows, num_rows, num_rows); +std::unique_ptr<TripletSparseMatrix> +TripletSparseMatrix::CreateSparseDiagonalMatrix(const double* values, + int num_rows) { + std::unique_ptr<TripletSparseMatrix> m = + std::make_unique<TripletSparseMatrix>(num_rows, num_rows, num_rows); for (int i = 0; i < num_rows; ++i) { m->mutable_rows()[i] = i; m->mutable_cols()[i] = i; @@ -272,7 +276,7 @@ void TripletSparseMatrix::ToTextFile(FILE* file) const { } } -TripletSparseMatrix* TripletSparseMatrix::CreateRandomMatrix( +std::unique_ptr<TripletSparseMatrix> TripletSparseMatrix::CreateRandomMatrix( const TripletSparseMatrix::RandomMatrixOptions& options) { CHECK_GT(options.num_rows, 0); CHECK_GT(options.num_cols, 0); @@ -297,7 +301,7 @@ TripletSparseMatrix* TripletSparseMatrix::CreateRandomMatrix( } } - return new TripletSparseMatrix( + return std::make_unique<TripletSparseMatrix>( options.num_rows, options.num_cols, rows, cols, values); } diff --git a/extern/ceres/internal/ceres/triplet_sparse_matrix.h b/extern/ceres/internal/ceres/triplet_sparse_matrix.h index cc9fee572a2..065c690dba3 100644 --- a/extern/ceres/internal/ceres/triplet_sparse_matrix.h +++ b/extern/ceres/internal/ceres/triplet_sparse_matrix.h @@ -34,8 +34,9 @@ #include <memory> #include <vector> +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/sparse_matrix.h" #include "ceres/types.h" @@ -46,7 +47,7 @@ namespace internal { // manipulate sparse matrices in triplet (i,j,s) form. This object is // inspired by the design of the cholmod_triplet struct used in the // SuiteSparse package and is memory layout compatible with it. -class CERES_EXPORT_INTERNAL TripletSparseMatrix : public SparseMatrix { +class CERES_NO_EXPORT TripletSparseMatrix final : public SparseMatrix { public: TripletSparseMatrix(); TripletSparseMatrix(int num_rows, int num_cols, int max_num_nonzeros); @@ -56,11 +57,11 @@ class CERES_EXPORT_INTERNAL TripletSparseMatrix : public SparseMatrix { const std::vector<int>& cols, const std::vector<double>& values); - explicit TripletSparseMatrix(const TripletSparseMatrix& orig); + TripletSparseMatrix(const TripletSparseMatrix& orig); TripletSparseMatrix& operator=(const TripletSparseMatrix& rhs); - virtual ~TripletSparseMatrix(); + ~TripletSparseMatrix() override; // Implementation of the SparseMatrix interface. void SetZero() final; @@ -115,8 +116,8 @@ class CERES_EXPORT_INTERNAL TripletSparseMatrix : public SparseMatrix { // Build a sparse diagonal matrix of size num_rows x num_rows from // the array values. Entries of the values array are copied into the // sparse matrix. - static TripletSparseMatrix* CreateSparseDiagonalMatrix(const double* values, - int num_rows); + static std::unique_ptr<TripletSparseMatrix> CreateSparseDiagonalMatrix( + const double* values, int num_rows); // Options struct to control the generation of random // TripletSparseMatrix objects. @@ -132,9 +133,7 @@ class CERES_EXPORT_INTERNAL TripletSparseMatrix : public SparseMatrix { // Create a random CompressedRowSparseMatrix whose entries are // normally distributed and whose structure is determined by // RandomMatrixOptions. - // - // Caller owns the result. - static TripletSparseMatrix* CreateRandomMatrix( + static std::unique_ptr<TripletSparseMatrix> CreateRandomMatrix( const TripletSparseMatrix::RandomMatrixOptions& options); private: @@ -158,4 +157,6 @@ class CERES_EXPORT_INTERNAL TripletSparseMatrix : public SparseMatrix { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_TRIPLET_SPARSE_MATRIX_H__ diff --git a/extern/ceres/internal/ceres/trust_region_minimizer.cc b/extern/ceres/internal/ceres/trust_region_minimizer.cc index bcf05b3ddfb..9ef5167ba6c 100644 --- a/extern/ceres/internal/ceres/trust_region_minimizer.cc +++ b/extern/ceres/internal/ceres/trust_region_minimizer.cc @@ -62,8 +62,6 @@ namespace ceres { namespace internal { -TrustRegionMinimizer::~TrustRegionMinimizer() {} - void TrustRegionMinimizer::Minimize(const Minimizer::Options& options, double* parameters, Solver::Summary* solver_summary) { @@ -75,11 +73,11 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options, // Create the TrustRegionStepEvaluator. The construction needs to be // delayed to this point because we need the cost for the starting // point to initialize the step evaluator. - step_evaluator_.reset(new TrustRegionStepEvaluator( + step_evaluator_ = std::make_unique<TrustRegionStepEvaluator>( x_cost_, options_.use_nonmonotonic_steps ? options_.max_consecutive_nonmonotonic_steps - : 0)); + : 0); while (FinalizeIterationAndCheckIfMinimizerCanContinue()) { iteration_start_time_in_secs_ = WallTimeInSeconds(); @@ -750,14 +748,12 @@ bool TrustRegionMinimizer::FunctionToleranceReached() { // Compute candidate_x_ = Plus(x_, delta_) // Evaluate the cost of candidate_x_ as candidate_cost_. // -// Failure to compute the step or the cost mean that candidate_cost_ -// is set to std::numeric_limits<double>::max(). Unlike -// EvaluateGradientAndJacobian, failure in this function is not fatal -// as we are only computing and evaluating a candidate point, and if -// for some reason we are unable to evaluate it, we consider it to be -// a point with very high cost. This allows the user to deal with edge -// cases/constraints as part of the LocalParameterization and -// CostFunction objects. +// Failure to compute the step or the cost mean that candidate_cost_ is set to +// std::numeric_limits<double>::max(). Unlike EvaluateGradientAndJacobian, +// failure in this function is not fatal as we are only computing and evaluating +// a candidate point, and if for some reason we are unable to evaluate it, we +// consider it to be a point with very high cost. This allows the user to deal +// with edge cases/constraints as part of the Manifold and CostFunction objects. void TrustRegionMinimizer::ComputeCandidatePointAndEvaluateCost() { if (!evaluator_->Plus(x_.data(), delta_.data(), candidate_x_.data())) { if (is_not_silent_) { diff --git a/extern/ceres/internal/ceres/trust_region_minimizer.h b/extern/ceres/internal/ceres/trust_region_minimizer.h index be4d40653c4..c6fc542a063 100644 --- a/extern/ceres/internal/ceres/trust_region_minimizer.h +++ b/extern/ceres/internal/ceres/trust_region_minimizer.h @@ -33,8 +33,9 @@ #include <memory> +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/minimizer.h" #include "ceres/solver.h" #include "ceres/sparse_matrix.h" @@ -48,10 +49,8 @@ namespace internal { // Generic trust region minimization algorithm. // // For example usage, see SolverImpl::Minimize. -class CERES_EXPORT_INTERNAL TrustRegionMinimizer : public Minimizer { +class CERES_NO_EXPORT TrustRegionMinimizer final : public Minimizer { public: - ~TrustRegionMinimizer(); - // This method is not thread safe. void Minimize(const Minimizer::Options& options, double* parameters, @@ -164,4 +163,6 @@ class CERES_EXPORT_INTERNAL TrustRegionMinimizer : public Minimizer { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_TRUST_REGION_MINIMIZER_H_ diff --git a/extern/ceres/internal/ceres/trust_region_preprocessor.cc b/extern/ceres/internal/ceres/trust_region_preprocessor.cc index 0943edbba85..edba47d88a5 100644 --- a/extern/ceres/internal/ceres/trust_region_preprocessor.cc +++ b/extern/ceres/internal/ceres/trust_region_preprocessor.cc @@ -55,13 +55,14 @@ using std::vector; namespace { -ParameterBlockOrdering* CreateDefaultLinearSolverOrdering( +std::shared_ptr<ParameterBlockOrdering> CreateDefaultLinearSolverOrdering( const Program& program) { - ParameterBlockOrdering* ordering = new ParameterBlockOrdering; + std::shared_ptr<ParameterBlockOrdering> ordering = + std::make_shared<ParameterBlockOrdering>(); const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks(); - for (int i = 0; i < parameter_blocks.size(); ++i) { + for (auto* parameter_block : parameter_blocks) { ordering->AddElementToGroup( - const_cast<double*>(parameter_blocks[i]->user_state()), 0); + const_cast<double*>(parameter_block->user_state()), 0); } return ordering; } @@ -160,8 +161,8 @@ bool SetupLinearSolver(PreprocessedProblem* pp) { // assume that they are giving all the freedom to us in choosing // the best possible ordering. This intent can be indicated by // putting all the parameter blocks in the same elimination group. - options.linear_solver_ordering.reset( - CreateDefaultLinearSolverOrdering(*pp->reduced_program)); + options.linear_solver_ordering = + CreateDefaultLinearSolverOrdering(*pp->reduced_program); } else { // If the user supplied an ordering, then check if the first // elimination group is still non-empty after the reduced problem @@ -247,7 +248,7 @@ bool SetupLinearSolver(PreprocessedProblem* pp) { } } - pp->linear_solver.reset(LinearSolver::Create(pp->linear_solver_options)); + pp->linear_solver = LinearSolver::Create(pp->linear_solver_options); return (pp->linear_solver != nullptr); } @@ -269,8 +270,8 @@ bool SetupEvaluator(PreprocessedProblem* pp) { pp->evaluator_options.context = pp->problem->context(); pp->evaluator_options.evaluation_callback = pp->reduced_program->mutable_evaluation_callback(); - pp->evaluator.reset(Evaluator::Create( - pp->evaluator_options, pp->reduced_program.get(), &pp->error)); + pp->evaluator = Evaluator::Create( + pp->evaluator_options, pp->reduced_program.get(), &pp->error); return (pp->evaluator != nullptr); } @@ -316,12 +317,12 @@ bool SetupInnerIterationMinimizer(PreprocessedProblem* pp) { } } else { // The user did not supply an ordering, so create one. - options.inner_iteration_ordering.reset( - CoordinateDescentMinimizer::CreateOrdering(*pp->reduced_program)); + options.inner_iteration_ordering = + CoordinateDescentMinimizer::CreateOrdering(*pp->reduced_program); } - pp->inner_iteration_minimizer.reset( - new CoordinateDescentMinimizer(pp->problem->context())); + pp->inner_iteration_minimizer = + std::make_unique<CoordinateDescentMinimizer>(pp->problem->context()); return pp->inner_iteration_minimizer->Init(*pp->reduced_program, pp->problem->parameter_map(), *options.inner_iteration_ordering, @@ -335,7 +336,7 @@ void SetupMinimizerOptions(PreprocessedProblem* pp) { SetupCommonMinimizerOptions(pp); pp->minimizer_options.is_constrained = pp->reduced_program->IsBoundsConstrained(); - pp->minimizer_options.jacobian.reset(pp->evaluator->CreateJacobian()); + pp->minimizer_options.jacobian = pp->evaluator->CreateJacobian(); pp->minimizer_options.inner_iteration_minimizer = pp->inner_iteration_minimizer; @@ -348,15 +349,13 @@ void SetupMinimizerOptions(PreprocessedProblem* pp) { strategy_options.trust_region_strategy_type = options.trust_region_strategy_type; strategy_options.dogleg_type = options.dogleg_type; - pp->minimizer_options.trust_region_strategy.reset( - TrustRegionStrategy::Create(strategy_options)); + pp->minimizer_options.trust_region_strategy = + TrustRegionStrategy::Create(strategy_options); CHECK(pp->minimizer_options.trust_region_strategy != nullptr); } } // namespace -TrustRegionPreprocessor::~TrustRegionPreprocessor() {} - bool TrustRegionPreprocessor::Preprocess(const Solver::Options& options, ProblemImpl* problem, PreprocessedProblem* pp) { @@ -370,10 +369,10 @@ bool TrustRegionPreprocessor::Preprocess(const Solver::Options& options, return false; } - pp->reduced_program.reset(program->CreateReducedProgram( - &pp->removed_parameter_blocks, &pp->fixed_cost, &pp->error)); + pp->reduced_program = program->CreateReducedProgram( + &pp->removed_parameter_blocks, &pp->fixed_cost, &pp->error); - if (pp->reduced_program.get() == NULL) { + if (pp->reduced_program.get() == nullptr) { return false; } diff --git a/extern/ceres/internal/ceres/trust_region_preprocessor.h b/extern/ceres/internal/ceres/trust_region_preprocessor.h index 2655abe4b2e..26ef8fad37d 100644 --- a/extern/ceres/internal/ceres/trust_region_preprocessor.h +++ b/extern/ceres/internal/ceres/trust_region_preprocessor.h @@ -31,15 +31,15 @@ #ifndef CERES_INTERNAL_TRUST_REGION_PREPROCESSOR_H_ #define CERES_INTERNAL_TRUST_REGION_PREPROCESSOR_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/preprocessor.h" namespace ceres { namespace internal { -class CERES_EXPORT_INTERNAL TrustRegionPreprocessor : public Preprocessor { +class CERES_NO_EXPORT TrustRegionPreprocessor final : public Preprocessor { public: - virtual ~TrustRegionPreprocessor(); bool Preprocess(const Solver::Options& options, ProblemImpl* problem, PreprocessedProblem* preprocessed_problem) override; @@ -48,4 +48,6 @@ class CERES_EXPORT_INTERNAL TrustRegionPreprocessor : public Preprocessor { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_TRUST_REGION_PREPROCESSOR_H_ diff --git a/extern/ceres/internal/ceres/trust_region_step_evaluator.h b/extern/ceres/internal/ceres/trust_region_step_evaluator.h index 03c00362dac..8e0c4e91f49 100644 --- a/extern/ceres/internal/ceres/trust_region_step_evaluator.h +++ b/extern/ceres/internal/ceres/trust_region_step_evaluator.h @@ -31,6 +31,8 @@ #ifndef CERES_INTERNAL_TRUST_REGION_STEP_EVALUATOR_H_ #define CERES_INTERNAL_TRUST_REGION_STEP_EVALUATOR_H_ +#include "ceres/internal/export.h" + namespace ceres { namespace internal { @@ -74,7 +76,7 @@ namespace internal { // x = x + delta; // step_evaluator->StepAccepted(cost, model_cost_change); // } -class TrustRegionStepEvaluator { +class CERES_NO_EXPORT TrustRegionStepEvaluator { public: // initial_cost is as the name implies the cost of the starting // state of the trust region minimizer. diff --git a/extern/ceres/internal/ceres/trust_region_strategy.cc b/extern/ceres/internal/ceres/trust_region_strategy.cc index 7e429d5e557..1096cd3c8aa 100644 --- a/extern/ceres/internal/ceres/trust_region_strategy.cc +++ b/extern/ceres/internal/ceres/trust_region_strategy.cc @@ -32,20 +32,23 @@ #include "ceres/trust_region_strategy.h" +#include <memory> + #include "ceres/dogleg_strategy.h" #include "ceres/levenberg_marquardt_strategy.h" namespace ceres { namespace internal { -TrustRegionStrategy::~TrustRegionStrategy() {} +TrustRegionStrategy::~TrustRegionStrategy() = default; -TrustRegionStrategy* TrustRegionStrategy::Create(const Options& options) { +std::unique_ptr<TrustRegionStrategy> TrustRegionStrategy::Create( + const Options& options) { switch (options.trust_region_strategy_type) { case LEVENBERG_MARQUARDT: - return new LevenbergMarquardtStrategy(options); + return std::make_unique<LevenbergMarquardtStrategy>(options); case DOGLEG: - return new DoglegStrategy(options); + return std::make_unique<DoglegStrategy>(options); default: LOG(FATAL) << "Unknown trust region strategy: " << options.trust_region_strategy_type; @@ -53,7 +56,7 @@ TrustRegionStrategy* TrustRegionStrategy::Create(const Options& options) { LOG(FATAL) << "Unknown trust region strategy: " << options.trust_region_strategy_type; - return NULL; + return nullptr; } } // namespace internal diff --git a/extern/ceres/internal/ceres/trust_region_strategy.h b/extern/ceres/internal/ceres/trust_region_strategy.h index 176f73a4876..33086cafb52 100644 --- a/extern/ceres/internal/ceres/trust_region_strategy.h +++ b/extern/ceres/internal/ceres/trust_region_strategy.h @@ -31,9 +31,11 @@ #ifndef CERES_INTERNAL_TRUST_REGION_STRATEGY_H_ #define CERES_INTERNAL_TRUST_REGION_STRATEGY_H_ +#include <memory> #include <string> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { @@ -54,7 +56,7 @@ class SparseMatrix; // the LevenbergMarquardtStrategy uses the inverse of the trust region // radius to scale the damping term, which controls the step size, but // does not set a hard limit on its size. -class CERES_EXPORT_INTERNAL TrustRegionStrategy { +class CERES_NO_EXPORT TrustRegionStrategy { public: struct Options { TrustRegionStrategyType trust_region_strategy_type = LEVENBERG_MARQUARDT; @@ -75,7 +77,7 @@ class CERES_EXPORT_INTERNAL TrustRegionStrategy { }; // Factory. - static TrustRegionStrategy* Create(const Options& options); + static std::unique_ptr<TrustRegionStrategy> Create(const Options& options); virtual ~TrustRegionStrategy(); @@ -142,4 +144,6 @@ class CERES_EXPORT_INTERNAL TrustRegionStrategy { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_TRUST_REGION_STRATEGY_H_ diff --git a/extern/ceres/internal/ceres/types.cc b/extern/ceres/internal/ceres/types.cc index 39bb2d8cc4d..48242678b46 100644 --- a/extern/ceres/internal/ceres/types.cc +++ b/extern/ceres/internal/ceres/types.cc @@ -34,6 +34,7 @@ #include <cctype> #include <string> +#include "ceres/internal/config.h" #include "glog/logging.h" namespace ceres { @@ -128,6 +129,7 @@ const char* DenseLinearAlgebraLibraryTypeToString( switch (type) { CASESTR(EIGEN); CASESTR(LAPACK); + CASESTR(CUDA); default: return "UNKNOWN"; } @@ -138,6 +140,7 @@ bool StringToDenseLinearAlgebraLibraryType( UpperCase(&value); STRENUM(EIGEN); STRENUM(LAPACK); + STRENUM(CUDA); return false; } @@ -417,6 +420,7 @@ bool IsDenseLinearAlgebraLibraryTypeAvailable( if (type == EIGEN) { return true; } + if (type == LAPACK) { #ifdef CERES_NO_LAPACK return false; @@ -425,6 +429,14 @@ bool IsDenseLinearAlgebraLibraryTypeAvailable( #endif } + if (type == CUDA) { +#ifdef CERES_NO_CUDA + return false; +#else + return true; +#endif + } + LOG(WARNING) << "Unknown dense linear algebra library " << type; return false; } diff --git a/extern/ceres/internal/ceres/visibility.cc b/extern/ceres/internal/ceres/visibility.cc index 82bf6f170b8..f666ce0c4bb 100644 --- a/extern/ceres/internal/ceres/visibility.cc +++ b/extern/ceres/internal/ceres/visibility.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -33,6 +33,7 @@ #include <algorithm> #include <cmath> #include <ctime> +#include <memory> #include <set> #include <unordered_map> #include <utility> @@ -62,8 +63,8 @@ void ComputeVisibility(const CompressedRowBlockStructure& block_structure, visibility->resize(0); visibility->resize(block_structure.cols.size() - num_eliminate_blocks); - for (int i = 0; i < block_structure.rows.size(); ++i) { - const vector<Cell>& cells = block_structure.rows[i].cells; + for (const auto& row : block_structure.rows) { + const vector<Cell>& cells = row.cells; int block_id = cells[0].block_id; // If the first block is not an e_block, then skip this row block. if (block_id >= num_eliminate_blocks) { @@ -79,16 +80,16 @@ void ComputeVisibility(const CompressedRowBlockStructure& block_structure, } } -WeightedGraph<int>* CreateSchurComplementGraph( +std::unique_ptr<WeightedGraph<int>> CreateSchurComplementGraph( const vector<set<int>>& visibility) { - const time_t start_time = time(NULL); + const time_t start_time = time(nullptr); // Compute the number of e_blocks/point blocks. Since the visibility // set for each e_block/camera contains the set of e_blocks/points // visible to it, we find the maximum across all visibility sets. int num_points = 0; - for (int i = 0; i < visibility.size(); i++) { - if (visibility[i].size() > 0) { - num_points = max(num_points, (*visibility[i].rbegin()) + 1); + for (const auto& visible : visibility) { + if (!visible.empty()) { + num_points = max(num_points, (*visible.rbegin()) + 1); } } @@ -100,7 +101,7 @@ WeightedGraph<int>* CreateSchurComplementGraph( vector<set<int>> inverse_visibility(num_points); for (int i = 0; i < visibility.size(); i++) { const set<int>& visibility_set = visibility[i]; - for (const int v : visibility_set) { + for (int v : visibility_set) { inverse_visibility[v].insert(i); } } @@ -111,17 +112,17 @@ WeightedGraph<int>* CreateSchurComplementGraph( // Count the number of points visible to each camera/f_block pair. for (const auto& inverse_visibility_set : inverse_visibility) { - for (set<int>::const_iterator camera1 = inverse_visibility_set.begin(); + for (auto camera1 = inverse_visibility_set.begin(); camera1 != inverse_visibility_set.end(); ++camera1) { - set<int>::const_iterator camera2 = camera1; + auto camera2 = camera1; for (++camera2; camera2 != inverse_visibility_set.end(); ++camera2) { ++(camera_pairs[make_pair(*camera1, *camera2)]); } } } - WeightedGraph<int>* graph = new WeightedGraph<int>; + auto graph = std::make_unique<WeightedGraph<int>>(); // Add vertices and initialize the pairs for self edges so that self // edges are guaranteed. This is needed for the Canonical views @@ -146,7 +147,7 @@ WeightedGraph<int>* CreateSchurComplementGraph( graph->AddEdge(camera1, camera2, weight); } - VLOG(2) << "Schur complement graph time: " << (time(NULL) - start_time); + VLOG(2) << "Schur complement graph time: " << (time(nullptr) - start_time); return graph; } diff --git a/extern/ceres/internal/ceres/visibility.h b/extern/ceres/internal/ceres/visibility.h index 68c6723fad7..d8f6968d98f 100644 --- a/extern/ceres/internal/ceres/visibility.h +++ b/extern/ceres/internal/ceres/visibility.h @@ -35,11 +35,13 @@ #ifndef CERES_INTERNAL_VISIBILITY_H_ #define CERES_INTERNAL_VISIBILITY_H_ +#include <memory> #include <set> #include <vector> #include "ceres/graph.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -54,7 +56,7 @@ struct CompressedRowBlockStructure; // // In a structure from motion problem, e_blocks correspond to 3D // points and f_blocks correspond to cameras. -CERES_EXPORT_INTERNAL void ComputeVisibility( +CERES_NO_EXPORT void ComputeVisibility( const CompressedRowBlockStructure& block_structure, int num_eliminate_blocks, std::vector<std::set<int>>* visibility); @@ -72,10 +74,12 @@ CERES_EXPORT_INTERNAL void ComputeVisibility( // // Caller acquires ownership of the returned WeightedGraph pointer // (heap-allocated). -CERES_EXPORT_INTERNAL WeightedGraph<int>* CreateSchurComplementGraph( +CERES_NO_EXPORT std::unique_ptr<WeightedGraph<int>> CreateSchurComplementGraph( const std::vector<std::set<int>>& visibility); } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_VISIBILITY_H_ diff --git a/extern/ceres/internal/ceres/visibility_based_preconditioner.cc b/extern/ceres/internal/ceres/visibility_based_preconditioner.cc index 0cf4afaae06..831a8663027 100644 --- a/extern/ceres/internal/ceres/visibility_based_preconditioner.cc +++ b/extern/ceres/internal/ceres/visibility_based_preconditioner.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -70,9 +70,8 @@ static constexpr double kCanonicalViewsSimilarityPenaltyWeight = 0.0; static constexpr double kSingleLinkageMinSimilarity = 0.9; VisibilityBasedPreconditioner::VisibilityBasedPreconditioner( - const CompressedRowBlockStructure& bs, - const Preconditioner::Options& options) - : options_(options), num_blocks_(0), num_clusters_(0) { + const CompressedRowBlockStructure& bs, Preconditioner::Options options) + : options_(std::move(options)), num_blocks_(0), num_clusters_(0) { CHECK_GT(options_.elimination_groups.size(), 1); CHECK_GT(options_.elimination_groups[0], 0); CHECK(options_.type == CLUSTER_JACOBI || options_.type == CLUSTER_TRIDIAGONAL) @@ -80,7 +79,7 @@ VisibilityBasedPreconditioner::VisibilityBasedPreconditioner( num_blocks_ = bs.cols.size() - options_.elimination_groups[0]; CHECK_GT(num_blocks_, 0) << "Jacobian should have at least 1 f_block for " << "visibility based preconditioning."; - CHECK(options_.context != NULL); + CHECK(options_.context != nullptr); // Vector of camera block sizes block_size_.resize(num_blocks_); @@ -88,7 +87,7 @@ VisibilityBasedPreconditioner::VisibilityBasedPreconditioner( block_size_[i] = bs.cols[i + options_.elimination_groups[0]].size; } - const time_t start_time = time(NULL); + const time_t start_time = time(nullptr); switch (options_.type) { case CLUSTER_JACOBI: ComputeClusterJacobiSparsity(bs); @@ -99,11 +98,11 @@ VisibilityBasedPreconditioner::VisibilityBasedPreconditioner( default: LOG(FATAL) << "Unknown preconditioner type"; } - const time_t structure_time = time(NULL); + const time_t structure_time = time(nullptr); InitStorage(bs); - const time_t storage_time = time(NULL); + const time_t storage_time = time(nullptr); InitEliminator(bs); - const time_t eliminator_time = time(NULL); + const time_t eliminator_time = time(nullptr); LinearSolver::Options sparse_cholesky_options; sparse_cholesky_options.sparse_linear_algebra_library_type = @@ -118,14 +117,14 @@ VisibilityBasedPreconditioner::VisibilityBasedPreconditioner( sparse_cholesky_options.use_postordering = true; sparse_cholesky_ = SparseCholesky::Create(sparse_cholesky_options); - const time_t init_time = time(NULL); + const time_t init_time = time(nullptr); VLOG(2) << "init time: " << init_time - start_time << " structure time: " << structure_time - start_time << " storage time:" << storage_time - structure_time << " eliminator time: " << eliminator_time - storage_time; } -VisibilityBasedPreconditioner::~VisibilityBasedPreconditioner() {} +VisibilityBasedPreconditioner::~VisibilityBasedPreconditioner() = default; // Determine the sparsity structure of the CLUSTER_JACOBI // preconditioner. It clusters cameras using their scene @@ -162,11 +161,9 @@ void VisibilityBasedPreconditioner::ComputeClusterTridiagonalSparsity( // maximum spanning forest of this graph. vector<set<int>> cluster_visibility; ComputeClusterVisibility(visibility, &cluster_visibility); - std::unique_ptr<WeightedGraph<int>> cluster_graph( - CreateClusterGraph(cluster_visibility)); + auto cluster_graph = CreateClusterGraph(cluster_visibility); CHECK(cluster_graph != nullptr); - std::unique_ptr<WeightedGraph<int>> forest( - Degree2MaximumSpanningForest(*cluster_graph)); + auto forest = Degree2MaximumSpanningForest(*cluster_graph); CHECK(forest != nullptr); ForestToClusterPairs(*forest, &cluster_pairs_); } @@ -175,7 +172,8 @@ void VisibilityBasedPreconditioner::ComputeClusterTridiagonalSparsity( void VisibilityBasedPreconditioner::InitStorage( const CompressedRowBlockStructure& bs) { ComputeBlockPairsInPreconditioner(bs); - m_.reset(new BlockRandomAccessSparseMatrix(block_size_, block_pairs_)); + m_ = std::make_unique<BlockRandomAccessSparseMatrix>(block_size_, + block_pairs_); } // Call the canonical views algorithm and cluster the cameras based on @@ -186,8 +184,7 @@ void VisibilityBasedPreconditioner::InitStorage( // memberships for each camera block. void VisibilityBasedPreconditioner::ClusterCameras( const vector<set<int>>& visibility) { - std::unique_ptr<WeightedGraph<int>> schur_complement_graph( - CreateSchurComplementGraph(visibility)); + auto schur_complement_graph = CreateSchurComplementGraph(visibility); CHECK(schur_complement_graph != nullptr); std::unordered_map<int, int> membership; @@ -285,14 +282,12 @@ void VisibilityBasedPreconditioner::ComputeBlockPairsInPreconditioner( } } - for (set<int>::const_iterator block1 = f_blocks.begin(); - block1 != f_blocks.end(); - ++block1) { - set<int>::const_iterator block2 = block1; + for (auto block1 = f_blocks.begin(); block1 != f_blocks.end(); ++block1) { + auto block2 = block1; ++block2; for (; block2 != f_blocks.end(); ++block2) { if (IsBlockPairInPreconditioner(*block1, *block2)) { - block_pairs_.insert(make_pair(*block1, *block2)); + block_pairs_.emplace(*block1, *block2); } } } @@ -304,8 +299,8 @@ void VisibilityBasedPreconditioner::ComputeBlockPairsInPreconditioner( CHECK_GE(row.cells.front().block_id, num_eliminate_blocks); for (int i = 0; i < row.cells.size(); ++i) { const int block1 = row.cells[i].block_id - num_eliminate_blocks; - for (int j = 0; j < row.cells.size(); ++j) { - const int block2 = row.cells[j].block_id - num_eliminate_blocks; + for (const auto& cell : row.cells) { + const int block2 = cell.block_id - num_eliminate_blocks; if (block1 <= block2) { if (IsBlockPairInPreconditioner(block1, block2)) { block_pairs_.insert(make_pair(block1, block2)); @@ -328,7 +323,7 @@ void VisibilityBasedPreconditioner::InitEliminator( eliminator_options.f_block_size = options_.f_block_size; eliminator_options.row_block_size = options_.row_block_size; eliminator_options.context = options_.context; - eliminator_.reset(SchurEliminatorBase::Create(eliminator_options)); + eliminator_ = SchurEliminatorBase::Create(eliminator_options); const bool kFullRankETE = true; eliminator_->Init( eliminator_options.elimination_groups[0], kFullRankETE, &bs); @@ -337,7 +332,7 @@ void VisibilityBasedPreconditioner::InitEliminator( // Update the values of the preconditioner matrix and factorize it. bool VisibilityBasedPreconditioner::UpdateImpl(const BlockSparseMatrix& A, const double* D) { - const time_t start_time = time(NULL); + const time_t start_time = time(nullptr); const int num_rows = m_->num_rows(); CHECK_GT(num_rows, 0); @@ -375,7 +370,7 @@ bool VisibilityBasedPreconditioner::UpdateImpl(const BlockSparseMatrix& A, status = Factorize(); } - VLOG(2) << "Compute time: " << time(NULL) - start_time; + VLOG(2) << "Compute time: " << time(nullptr) - start_time; return (status == LINEAR_SOLVER_SUCCESS); } @@ -395,7 +390,7 @@ void VisibilityBasedPreconditioner::ScaleOffDiagonalCells() { int r, c, row_stride, col_stride; CellInfo* cell_info = m_->GetCell(block1, block2, &r, &c, &row_stride, &col_stride); - CHECK(cell_info != NULL) + CHECK(cell_info != nullptr) << "Cell missing for block pair (" << block1 << "," << block2 << ")" << " cluster pair (" << cluster_membership_[block1] << " " << cluster_membership_[block2] << ")"; @@ -420,11 +415,10 @@ LinearSolverTerminationType VisibilityBasedPreconditioner::Factorize() { const CompressedRowSparseMatrix::StorageType storage_type = sparse_cholesky_->StorageType(); if (storage_type == CompressedRowSparseMatrix::UPPER_TRIANGULAR) { - lhs.reset(CompressedRowSparseMatrix::FromTripletSparseMatrix(*tsm)); + lhs = CompressedRowSparseMatrix::FromTripletSparseMatrix(*tsm); lhs->set_storage_type(CompressedRowSparseMatrix::UPPER_TRIANGULAR); } else { - lhs.reset( - CompressedRowSparseMatrix::FromTripletSparseMatrixTransposed(*tsm)); + lhs = CompressedRowSparseMatrix::FromTripletSparseMatrixTransposed(*tsm); lhs->set_storage_type(CompressedRowSparseMatrix::LOWER_TRIANGULAR); } @@ -503,9 +497,10 @@ void VisibilityBasedPreconditioner::ComputeClusterVisibility( // Construct a graph whose vertices are the clusters, and the edge // weights are the number of 3D points visible to cameras in both the // vertices. -WeightedGraph<int>* VisibilityBasedPreconditioner::CreateClusterGraph( +std::unique_ptr<WeightedGraph<int>> +VisibilityBasedPreconditioner::CreateClusterGraph( const vector<set<int>>& cluster_visibility) const { - WeightedGraph<int>* cluster_graph = new WeightedGraph<int>; + auto cluster_graph = std::make_unique<WeightedGraph<int>>(); for (int i = 0; i < num_clusters_; ++i) { cluster_graph->AddVertex(i); diff --git a/extern/ceres/internal/ceres/visibility_based_preconditioner.h b/extern/ceres/internal/ceres/visibility_based_preconditioner.h index 0457b9a376a..8079dc3f3ce 100644 --- a/extern/ceres/internal/ceres/visibility_based_preconditioner.h +++ b/extern/ceres/internal/ceres/visibility_based_preconditioner.h @@ -122,9 +122,10 @@ class SchurEliminatorBase; // options.elimination_groups.push_back(num_cameras); // VisibilityBasedPreconditioner preconditioner( // *A.block_structure(), options); -// preconditioner.Update(A, NULL); +// preconditioner.Update(A, nullptr); // preconditioner.RightMultiply(x, y); -class VisibilityBasedPreconditioner : public BlockSparseMatrixPreconditioner { +class CERES_NO_EXPORT VisibilityBasedPreconditioner + : public BlockSparseMatrixPreconditioner { public: // Initialize the symbolic structure of the preconditioner. bs is // the block structure of the linear system to be solved. It is used @@ -133,11 +134,11 @@ class VisibilityBasedPreconditioner : public BlockSparseMatrixPreconditioner { // It has the same structural requirement as other Schur complement // based solvers. Please see schur_eliminator.h for more details. VisibilityBasedPreconditioner(const CompressedRowBlockStructure& bs, - const Preconditioner::Options& options); + Preconditioner::Options options); VisibilityBasedPreconditioner(const VisibilityBasedPreconditioner&) = delete; void operator=(const VisibilityBasedPreconditioner&) = delete; - virtual ~VisibilityBasedPreconditioner(); + ~VisibilityBasedPreconditioner() override; // Preconditioner interface void RightMultiply(const double* x, double* y) const final; @@ -160,7 +161,7 @@ class VisibilityBasedPreconditioner : public BlockSparseMatrixPreconditioner { void ComputeClusterVisibility( const std::vector<std::set<int>>& visibility, std::vector<std::set<int>>* cluster_visibility) const; - WeightedGraph<int>* CreateClusterGraph( + std::unique_ptr<WeightedGraph<int>> CreateClusterGraph( const std::vector<std::set<int>>& visibility) const; void ForestToClusterPairs( const WeightedGraph<int>& forest, diff --git a/extern/ceres/internal/ceres/wall_time.cc b/extern/ceres/internal/ceres/wall_time.cc index 716392741e9..a54ab640b3e 100644 --- a/extern/ceres/internal/ceres/wall_time.cc +++ b/extern/ceres/internal/ceres/wall_time.cc @@ -30,6 +30,8 @@ #include "ceres/wall_time.h" +#include "ceres/internal/config.h" + #ifdef CERES_USE_OPENMP #include <omp.h> #else @@ -58,7 +60,7 @@ double WallTimeInSeconds() { static_cast<double>(frequency.QuadPart); #else timeval time_val; - gettimeofday(&time_val, NULL); + gettimeofday(&time_val, nullptr); return (time_val.tv_sec + time_val.tv_usec * 1e-6); #endif #endif diff --git a/extern/ceres/internal/ceres/wall_time.h b/extern/ceres/internal/ceres/wall_time.h index 9c92e9e60ef..f093eed0418 100644 --- a/extern/ceres/internal/ceres/wall_time.h +++ b/extern/ceres/internal/ceres/wall_time.h @@ -34,7 +34,8 @@ #include <map> #include <string> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/stringprintf.h" #include "glog/logging.h" @@ -45,7 +46,7 @@ namespace internal { // OpenMP is available then the high precision openmp_get_wtime() // function is used. Otherwise on unixes, gettimeofday is used. The // granularity is in seconds on windows systems. -CERES_EXPORT_INTERNAL double WallTimeInSeconds(); +CERES_NO_EXPORT double WallTimeInSeconds(); // Log a series of events, recording for each event the time elapsed // since the last event and since the creation of the object. @@ -71,7 +72,7 @@ CERES_EXPORT_INTERNAL double WallTimeInSeconds(); // Bar1: time1 time1 // Bar2: time2 time1 + time2; // Total: time3 time1 + time2 + time3; -class EventLogger { +class CERES_NO_EXPORT EventLogger { public: explicit EventLogger(const std::string& logger_name); ~EventLogger(); @@ -86,4 +87,6 @@ class EventLogger { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_WALL_TIME_H_ diff --git a/extern/ceres/mkfiles.sh b/extern/ceres/mkfiles.sh deleted file mode 100755 index cb07663e94a..00000000000 --- a/extern/ceres/mkfiles.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -find ./include/ -type f | sed -r 's/^\.\///' | sort > files.txt -find ./internal/ -type f | sed -r 's/^\.\///' | sort >> files.txt -find ./config/ -type f | sed -r 's/^\.\///' | sort >> files.txt diff --git a/extern/ceres/patches/series b/extern/ceres/patches/series deleted file mode 100644 index e69de29bb2d..00000000000 --- a/extern/ceres/patches/series +++ /dev/null diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 9b4799d252f..9acc9e99ad0 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -24,12 +24,19 @@ from . import camera enum_devices = ( ('CPU', "CPU", "Use CPU for rendering"), - ('GPU', "GPU Compute", "Use GPU compute device for rendering, configured in the system tab in the user preferences"), + ('GPU', "GPU Compute", + "Use GPU compute device for rendering, configured in the system tab in the user preferences"), ) enum_feature_set = ( - ('SUPPORTED', "Supported", "Only use finished and supported features"), - ('EXPERIMENTAL', "Experimental", "Use experimental and incomplete features that might be broken or change in the future", 'ERROR', 1), + ('SUPPORTED', + "Supported", + "Only use finished and supported features"), + ('EXPERIMENTAL', + "Experimental", + "Use experimental and incomplete features that might be broken or change in the future", + 'ERROR', + 1), ) enum_displacement_methods = ( @@ -81,9 +88,14 @@ enum_sampling_pattern = ( ) enum_volume_sampling = ( - ('DISTANCE', "Distance", "Use distance sampling, best for dense volumes with lights far away"), - ('EQUIANGULAR', "Equiangular", "Use equiangular sampling, best for volumes with low density with light inside or near the volume"), - ('MULTIPLE_IMPORTANCE', "Multiple Importance", + ('DISTANCE', + "Distance", + "Use distance sampling, best for dense volumes with lights far away"), + ('EQUIANGULAR', + "Equiangular", + "Use equiangular sampling, best for volumes with low density with light inside or near the volume"), + ('MULTIPLE_IMPORTANCE', + "Multiple Importance", "Combine distance and equi-angular sampling for volumes where neither method is ideal"), ) @@ -93,10 +105,15 @@ enum_volume_interpolation = ( ) enum_world_mis = ( - ('NONE', "None", "Don't sample the background, faster but might cause noise for non-solid backgrounds"), - ('AUTOMATIC', "Auto", "Automatically try to determine the best setting"), - ('MANUAL', "Manual", "Manually set the resolution of the sampling map, higher values are slower and require more memory but reduce noise") -) + ('NONE', + "None", + "Don't sample the background, faster but might cause noise for non-solid backgrounds"), + ('AUTOMATIC', + "Auto", + "Automatically try to determine the best setting"), + ('MANUAL', + "Manual", + "Manually set the resolution of the sampling map, higher values are slower and require more memory but reduce noise")) enum_device_type = ( ('CPU', "CPU", "CPU", 0), @@ -210,17 +227,33 @@ enum_denoising_input_passes = ( ) enum_denoising_prefilter = ( - ('NONE', "None", "No prefiltering, use when guiding passes are noise-free", 1), - ('FAST', "Fast", "Denoise color and guiding passes together. Improves quality when guiding passes are noisy using least amount of extra processing time", 2), - ('ACCURATE', "Accurate", "Prefilter noisy guiding passes before denoising color. Improves quality when guiding passes are noisy using extra processing time", 3), + ('NONE', + "None", + "No prefiltering, use when guiding passes are noise-free", + 1), + ('FAST', + "Fast", + "Denoise color and guiding passes together. Improves quality when guiding passes are noisy using least amount of extra processing time", + 2), + ('ACCURATE', + "Accurate", + "Prefilter noisy guiding passes before denoising color. Improves quality when guiding passes are noisy using extra processing time", + 3), ) enum_direct_light_sampling_type = ( - ('MULTIPLE_IMPORTANCE_SAMPLING', "Multiple Importance Sampling", - "Multiple importance sampling is used to combine direct light contributions from next-event estimation and forward path tracing", 0), - ('FORWARD_PATH_TRACING', "Forward Path Tracing", "Direct light contributions are only sampled using forward path tracing", 1), - ('NEXT_EVENT_ESTIMATION', "Next-Event Estimation", - "Direct light contributions are only sampled using next-event estimation", 2), + ('MULTIPLE_IMPORTANCE_SAMPLING', + "Multiple Importance Sampling", + "Multiple importance sampling is used to combine direct light contributions from next-event estimation and forward path tracing", + 0), + ('FORWARD_PATH_TRACING', + "Forward Path Tracing", + "Direct light contributions are only sampled using forward path tracing", + 1), + ('NEXT_EVENT_ESTIMATION', + "Next-Event Estimation", + "Direct light contributions are only sampled using next-event estimation", + 2), ) @@ -357,7 +390,8 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): scrambling_distance: FloatProperty( name="Scrambling Distance", default=1.0, - min=0.0, soft_max=1.0, + min=0.0, + soft_max=1.0, description="Reduce randomization between pixels to improve GPU rendering performance, at the cost of possible rendering artifacts if set too low", ) preview_scrambling_distance: BoolProperty( @@ -383,7 +417,8 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): name="Light Sampling Threshold", description="Probabilistically terminate light samples when the light contribution is below this threshold (more noise but faster rendering). " "Zero disables the test and never ignores lights", - min=0.0, max=1.0, + min=0.0, + max=1.0, default=0.01, ) @@ -395,7 +430,8 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): adaptive_threshold: FloatProperty( name="Adaptive Sampling Threshold", description="Noise level step to stop sampling at, lower values reduce noise at the cost of render time. Zero for automatic setting based on number of AA samples", - min=0.0, max=1.0, + min=0.0, + max=1.0, soft_min=0.001, default=0.01, precision=4, @@ -403,7 +439,8 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): adaptive_min_samples: IntProperty( name="Adaptive Min Samples", description="Minimum AA samples for adaptive sampling, to discover noisy features before stopping sampling. Zero for automatic setting based on noise threshold", - min=0, max=4096, + min=0, + max=4096, default=0, ) @@ -415,7 +452,8 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): preview_adaptive_threshold: FloatProperty( name="Adaptive Sampling Threshold", description="Noise level step to stop sampling at, lower values reduce noise at the cost of render time. Zero for automatic setting based on number of AA samples, for viewport renders", - min=0.0, max=1.0, + min=0.0, + max=1.0, soft_min=0.001, default=0.1, precision=4, @@ -423,7 +461,8 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): preview_adaptive_min_samples: IntProperty( name="Adaptive Min Samples", description="Minimum AA samples for adaptive sampling, to discover noisy features before stopping sampling. Zero for automatic setting based on noise threshold, for viewport renders", - min=0, max=4096, + min=0, + max=4096, default=0, ) @@ -550,7 +589,8 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): max_subdivisions: IntProperty( name="Max Subdivisions", description="Stop subdividing when this level is reached even if the dice rate would produce finer tessellation", - min=0, max=16, + min=0, + max=16, default=12, ) @@ -817,8 +857,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): debug_use_optix_debug: BoolProperty( name="OptiX Module Debug", description="Load OptiX module in debug mode: lower logging verbosity level, enable validations, and lower optimization level", - default=False - ) + default=False) @classmethod def register(cls): @@ -1185,7 +1224,8 @@ class CyclesObjectSettings(bpy.types.PropertyGroup): motion_steps: IntProperty( name="Motion Steps", description="Control accuracy of motion blur, more steps gives more memory usage (actual number of steps is 2^(steps - 1))", - min=1, max=7, + min=1, + max=7, default=1, ) @@ -1224,7 +1264,8 @@ class CyclesObjectSettings(bpy.types.PropertyGroup): shadow_terminator_geometry_offset: FloatProperty( name="Shadow Terminator Geometry Offset", description="Offset rays from the surface to reduce shadow terminator artifact on low poly geometry. Only affects triangles at grazing angles to light", - min=0.0, max=1.0, + min=0.0, + max=1.0, default=0.1, ) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 886f5345020..9d2dbdf6732 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -1082,8 +1082,18 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel): def has_geometry_visibility(ob): - return ob and ((ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'LIGHT', 'VOLUME', 'POINTCLOUD', 'CURVES'}) or - (ob.instance_type == 'COLLECTION' and ob.instance_collection)) + return ob and ( + (ob.type in { + 'MESH', + 'CURVE', + 'SURFACE', + 'FONT', + 'META', + 'LIGHT', + 'VOLUME', + 'POINTCLOUD', + 'CURVES', + }) or (ob.instance_type == 'COLLECTION' and ob.instance_collection)) class CYCLES_OBJECT_PT_shading(CyclesButtonsPanel, Panel): diff --git a/intern/cycles/blender/image.cpp b/intern/cycles/blender/image.cpp index ca4c8f5904a..e01b72c1653 100644 --- a/intern/cycles/blender/image.cpp +++ b/intern/cycles/blender/image.cpp @@ -13,9 +13,11 @@ CCL_NAMESPACE_BEGIN BlenderImageLoader::BlenderImageLoader(BL::Image b_image, const int frame, + const int tile_number, const bool is_preview_render) : b_image(b_image), frame(frame), + tile_number(tile_number), /* Don't free cache for preview render to avoid race condition from T93560, to be fixed properly later as we are close to release. */ free_cache(!is_preview_render && !b_image.has_data()) @@ -66,12 +68,11 @@ bool BlenderImageLoader::load_pixels(const ImageMetaData &metadata, { const size_t num_pixels = ((size_t)metadata.width) * metadata.height; const int channels = metadata.channels; - const int tile = 0; /* TODO(lukas): Support tiles here? */ if (b_image.is_float()) { /* image data */ float *image_pixels; - image_pixels = image_get_float_pixels_for_frame(b_image, frame, tile); + image_pixels = image_get_float_pixels_for_frame(b_image, frame, tile_number); if (image_pixels && num_pixels * channels == pixels_size) { memcpy(pixels, image_pixels, pixels_size * sizeof(float)); @@ -99,7 +100,7 @@ bool BlenderImageLoader::load_pixels(const ImageMetaData &metadata, } } else { - unsigned char *image_pixels = image_get_pixels_for_frame(b_image, frame, tile); + unsigned char *image_pixels = image_get_pixels_for_frame(b_image, frame, tile_number); if (image_pixels && num_pixels * channels == pixels_size) { memcpy(pixels, image_pixels, pixels_size * sizeof(unsigned char)); @@ -153,7 +154,13 @@ string BlenderImageLoader::name() const bool BlenderImageLoader::equals(const ImageLoader &other) const { const BlenderImageLoader &other_loader = (const BlenderImageLoader &)other; - return b_image == other_loader.b_image && frame == other_loader.frame; + return b_image == other_loader.b_image && frame == other_loader.frame && + tile_number == other_loader.tile_number; +} + +int BlenderImageLoader::get_tile_number() const +{ + return tile_number; } /* Point Density */ diff --git a/intern/cycles/blender/image.h b/intern/cycles/blender/image.h index ee576b31f7e..c2cc0f51b9b 100644 --- a/intern/cycles/blender/image.h +++ b/intern/cycles/blender/image.h @@ -12,7 +12,10 @@ CCL_NAMESPACE_BEGIN class BlenderImageLoader : public ImageLoader { public: - BlenderImageLoader(BL::Image b_image, const int frame, const bool is_preview_render); + BlenderImageLoader(BL::Image b_image, + const int frame, + const int tile_number, + const bool is_preview_render); bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override; bool load_pixels(const ImageMetaData &metadata, @@ -22,8 +25,11 @@ class BlenderImageLoader : public ImageLoader { string name() const override; bool equals(const ImageLoader &other) const override; + int get_tile_number() const override; + BL::Image b_image; int frame; + int tile_number; bool free_cache; }; diff --git a/intern/cycles/blender/mesh.cpp b/intern/cycles/blender/mesh.cpp index de67e27923d..c76ce3801d4 100644 --- a/intern/cycles/blender/mesh.cpp +++ b/intern/cycles/blender/mesh.cpp @@ -316,7 +316,7 @@ static void fill_generic_attribute(BL::Mesh &b_mesh, break; } case BL::Attribute::domain_EDGE: { - /* Averge edge attributes at vertices. */ + /* Average edge attributes at vertices. */ const size_t num_verts = b_mesh.vertices.length(); vector<int> count(num_verts, 0); diff --git a/intern/cycles/blender/shader.cpp b/intern/cycles/blender/shader.cpp index d3527567b96..81a64457c88 100644 --- a/intern/cycles/blender/shader.cpp +++ b/intern/cycles/blender/shader.cpp @@ -355,6 +355,18 @@ static ShaderNode *add_node(Scene *scene, else if (b_node.is_a(&RNA_ShaderNodeCombineHSV)) { node = graph->create_node<CombineHSVNode>(); } + else if (b_node.is_a(&RNA_ShaderNodeSeparateColor)) { + BL::ShaderNodeSeparateColor b_separate_node(b_node); + SeparateColorNode *separate_node = graph->create_node<SeparateColorNode>(); + separate_node->set_color_type((NodeCombSepColorType)b_separate_node.mode()); + node = separate_node; + } + else if (b_node.is_a(&RNA_ShaderNodeCombineColor)) { + BL::ShaderNodeCombineColor b_combine_node(b_node); + CombineColorNode *combine_node = graph->create_node<CombineColorNode>(); + combine_node->set_color_type((NodeCombSepColorType)b_combine_node.mode()); + node = combine_node; + } else if (b_node.is_a(&RNA_ShaderNodeSeparateXYZ)) { node = graph->create_node<SeparateXYZNode>(); } @@ -764,9 +776,21 @@ static ShaderNode *add_node(Scene *scene, */ int scene_frame = b_scene.frame_current(); int image_frame = image_user_frame_number(b_image_user, b_image, scene_frame); - image->handle = scene->image_manager->add_image( - new BlenderImageLoader(b_image, image_frame, b_engine.is_preview()), - image->image_params()); + if (b_image.source() != BL::Image::source_TILED) { + image->handle = scene->image_manager->add_image( + new BlenderImageLoader(b_image, image_frame, 0, b_engine.is_preview()), + image->image_params()); + } + else { + vector<ImageLoader *> loaders; + loaders.reserve(image->get_tiles().size()); + for (int tile_number : image->get_tiles()) { + loaders.push_back( + new BlenderImageLoader(b_image, image_frame, tile_number, b_engine.is_preview())); + } + + image->handle = scene->image_manager->add_image(loaders, image->image_params()); + } } else { ustring filename = ustring( @@ -802,7 +826,7 @@ static ShaderNode *add_node(Scene *scene, int scene_frame = b_scene.frame_current(); int image_frame = image_user_frame_number(b_image_user, b_image, scene_frame); env->handle = scene->image_manager->add_image( - new BlenderImageLoader(b_image, image_frame, b_engine.is_preview()), + new BlenderImageLoader(b_image, image_frame, 0, b_engine.is_preview()), env->image_params()); } else { diff --git a/intern/cycles/device/metal/device_impl.h b/intern/cycles/device/metal/device_impl.h index 27c58ce6d2f..7506b9b069f 100644 --- a/intern/cycles/device/metal/device_impl.h +++ b/intern/cycles/device/metal/device_impl.h @@ -28,7 +28,8 @@ class MetalDevice : public Device { id<MTLCommandQueue> mtlGeneralCommandQueue = nil; id<MTLArgumentEncoder> mtlAncillaryArgEncoder = nil; /* encoder used for fetching device pointers from MTLBuffers */ - string source_used_for_compile[PSO_NUM]; + string source[PSO_NUM]; + string source_md5[PSO_NUM]; KernelParamsMetal launch_params = {0}; @@ -72,7 +73,6 @@ class MetalDevice : public Device { id<MTLBuffer> texture_bindings_3d = nil; std::vector<id<MTLTexture>> texture_slot_map; - MetalDeviceKernels kernels; bool use_metalrt = false; bool use_function_specialisation = false; @@ -110,6 +110,8 @@ class MetalDevice : public Device { virtual void build_bvh(BVH *bvh, Progress &progress, bool refit) override; + id<MTLLibrary> compile(string const &source); + /* ------------------------------------------------------------------ */ /* low-level memory management */ diff --git a/intern/cycles/device/metal/device_impl.mm b/intern/cycles/device/metal/device_impl.mm index c01f51fb506..e1438a9d6e2 100644 --- a/intern/cycles/device/metal/device_impl.mm +++ b/intern/cycles/device/metal/device_impl.mm @@ -275,96 +275,44 @@ bool MetalDevice::load_kernels(const uint _kernel_features) * active, but may still need to be rendered without motion blur if that isn't active as well. */ motion_blur = kernel_features & KERNEL_FEATURE_OBJECT_MOTION; - NSError *error = NULL; + source[PSO_GENERIC] = get_source(kernel_features); + mtlLibrary[PSO_GENERIC] = compile(source[PSO_GENERIC]); - for (int i = 0; i < PSO_NUM; i++) { - if (mtlLibrary[i]) { - [mtlLibrary[i] release]; - mtlLibrary[i] = nil; - } - } + MD5Hash md5; + md5.append(source[PSO_GENERIC]); + source_md5[PSO_GENERIC] = md5.get_hex(); + + metal_printf("Front-end compilation finished (generic)\n"); + + bool result = MetalDeviceKernels::load(this, false); + + reserve_local_memory(kernel_features); + + return result; +} +id<MTLLibrary> MetalDevice::compile(string const &source) +{ MTLCompileOptions *options = [[MTLCompileOptions alloc] init]; options.fastMathEnabled = YES; if (@available(macOS 12.0, *)) { options.languageVersion = MTLLanguageVersion2_4; } - else { - return false; - } - string metalsrc; - - /* local helper: dump source to disk and return filepath */ - auto dump_source = [&](int kernel_type) -> string { - string &source = source_used_for_compile[kernel_type]; - string metalsrc = path_cache_get(path_join("kernels", - string_printf("%s.%s.metal", - kernel_type_as_string(kernel_type), - util_md5_string(source).c_str()))); - path_write_text(metalsrc, source); - return metalsrc; - }; - - /* local helper: fetch the kernel source code, adjust it for specific PSO_.. kernel_type flavor, - * then compile it into a MTLLibrary */ - auto fetch_and_compile_source = [&](int kernel_type) { - /* Record the source used to compile this library, for hash building later. */ - string &source = source_used_for_compile[kernel_type]; - - switch (kernel_type) { - case PSO_GENERIC: { - source = get_source(kernel_features); - break; - } - case PSO_SPECIALISED: { - /* PSO_SPECIALISED derives from PSO_GENERIC */ - string &generic_source = source_used_for_compile[PSO_GENERIC]; - if (generic_source.empty()) { - generic_source = get_source(kernel_features); - } - source = "#define __KERNEL_METAL_USE_FUNCTION_SPECIALISATION__\n" + generic_source; - break; - } - default: - assert(0); - } - - /* create MTLLibrary (front-end compilation) */ - mtlLibrary[kernel_type] = [mtlDevice newLibraryWithSource:@(source.c_str()) + NSError *error = NULL; + id<MTLLibrary> mtlLibrary = [mtlDevice newLibraryWithSource:@(source.c_str()) options:options error:&error]; - bool do_source_dump = (getenv("CYCLES_METAL_DUMP_SOURCE") != nullptr); - - if (!mtlLibrary[kernel_type] || do_source_dump) { - string metalsrc = dump_source(kernel_type); - - if (!mtlLibrary[kernel_type]) { - NSString *err = [error localizedDescription]; - set_error(string_printf("Failed to compile library:\n%s", [err UTF8String])); - - return false; - } - } - return true; - }; - - fetch_and_compile_source(PSO_GENERIC); - - if (use_function_specialisation) { - fetch_and_compile_source(PSO_SPECIALISED); + if (!mtlLibrary) { + NSString *err = [error localizedDescription]; + set_error(string_printf("Failed to compile library:\n%s", [err UTF8String])); } - metal_printf("Front-end compilation finished\n"); - - bool result = kernels.load(this, PSO_GENERIC); - [options release]; - reserve_local_memory(kernel_features); - return result; + return mtlLibrary; } void MetalDevice::reserve_local_memory(const uint kernel_features) diff --git a/intern/cycles/device/metal/kernel.h b/intern/cycles/device/metal/kernel.h index b12491d820d..69b2a686ecc 100644 --- a/intern/cycles/device/metal/kernel.h +++ b/intern/cycles/device/metal/kernel.h @@ -54,103 +54,41 @@ enum { const char *kernel_type_as_string(int kernel_type); struct MetalKernelPipeline { - void release() - { - if (pipeline) { - [pipeline release]; - pipeline = nil; - if (@available(macOS 11.0, *)) { - for (int i = 0; i < METALRT_TABLE_NUM; i++) { - if (intersection_func_table[i]) { - [intersection_func_table[i] release]; - intersection_func_table[i] = nil; - } - } - } - } - if (function) { - [function release]; - function = nil; - } - if (@available(macOS 11.0, *)) { - for (int i = 0; i < METALRT_TABLE_NUM; i++) { - if (intersection_func_table[i]) { - [intersection_func_table[i] release]; - } - } - } - } - bool loaded = false; - id<MTLFunction> function = nil; - id<MTLComputePipelineState> pipeline = nil; - - API_AVAILABLE(macos(11.0)) - id<MTLIntersectionFunctionTable> intersection_func_table[METALRT_TABLE_NUM] = {nil}; -}; - -struct MetalKernelLoadDesc { - int pso_index = 0; - const char *function_name = nullptr; - int kernel_index = 0; - int threads_per_threadgroup = 0; - MTLFunctionConstantValues *constant_values = nullptr; - NSArray *linked_functions = nullptr; - - struct IntersectorFunctions { - NSArray *defaults; - NSArray *shadow; - NSArray *local; - NSArray *operator[](int index) const - { - if (index == METALRT_TABLE_DEFAULT) - return defaults; - if (index == METALRT_TABLE_SHADOW) - return shadow; - return local; - } - } intersector_functions = {nullptr}; -}; - -/* Metal kernel and associate occupancy information. */ -class MetalDeviceKernel { - public: - ~MetalDeviceKernel(); + void compile(); - bool load(MetalDevice *device, MetalKernelLoadDesc const &desc, class MD5Hash const &md5); + id<MTLLibrary> mtlLibrary = nil; + bool scene_specialized; + string source_md5; - void mark_loaded(int pso_index) - { - pso[pso_index].loaded = true; - } + bool use_metalrt; + bool metalrt_hair; + bool metalrt_hair_thick; + bool metalrt_pointcloud; - int get_num_threads_per_block() const - { - return num_threads_per_block; - } - const MetalKernelPipeline &get_pso() const; + int threads_per_threadgroup; - double load_duration = 0.0; + DeviceKernel device_kernel; + bool loaded = false; + id<MTLDevice> mtlDevice = nil; + id<MTLFunction> function = nil; + id<MTLComputePipelineState> pipeline = nil; + int num_threads_per_block = 0; - private: - MetalKernelPipeline pso[PSO_NUM]; + string error_str; - int num_threads_per_block = 0; + API_AVAILABLE(macos(11.0)) + id<MTLIntersectionFunctionTable> intersection_func_table[METALRT_TABLE_NUM] = {nil}; + id<MTLFunction> rt_intersection_function[METALRT_FUNC_NUM] = {nil}; }; /* Cache of Metal kernels for each DeviceKernel. */ -class MetalDeviceKernels { - public: - bool load(MetalDevice *device, int kernel_type); - bool available(DeviceKernel kernel) const; - const MetalDeviceKernel &get(DeviceKernel kernel) const; +namespace MetalDeviceKernels { - MetalDeviceKernel kernels_[DEVICE_KERNEL_NUM]; +bool load(MetalDevice *device, bool scene_specialized); +const MetalKernelPipeline *get_best_pipeline(const MetalDevice *device, DeviceKernel kernel); - id<MTLFunction> rt_intersection_funcs[PSO_NUM][METALRT_FUNC_NUM] = {{nil}}; - - string loaded_md5[PSO_NUM]; -}; +} /* namespace MetalDeviceKernels */ CCL_NAMESPACE_END diff --git a/intern/cycles/device/metal/kernel.mm b/intern/cycles/device/metal/kernel.mm index 9555ca03c8e..304efc813ec 100644 --- a/intern/cycles/device/metal/kernel.mm +++ b/intern/cycles/device/metal/kernel.mm @@ -9,6 +9,7 @@ # include "util/path.h" # include "util/tbb.h" # include "util/time.h" +# include "util/unique_ptr.h" CCL_NAMESPACE_BEGIN @@ -28,82 +29,376 @@ const char *kernel_type_as_string(int kernel_type) return ""; } -MetalDeviceKernel::~MetalDeviceKernel() +bool kernel_has_intersection(DeviceKernel device_kernel) { - for (int i = 0; i < PSO_NUM; i++) { - pso[i].release(); + return (device_kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST || + device_kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW || + device_kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE || + device_kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK || + device_kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE); +} + +struct ShaderCache { + ShaderCache(id<MTLDevice> _mtlDevice) : mtlDevice(_mtlDevice) + { + } + ~ShaderCache(); + + /* Get the fastest available pipeline for the specified kernel. */ + MetalKernelPipeline *get_best_pipeline(DeviceKernel kernel, const MetalDevice *device); + + /* Non-blocking request for a kernel, optionally specialized to the scene being rendered by + * device. */ + void load_kernel(DeviceKernel kernel, MetalDevice *device, bool scene_specialized); + + void wait_for_all(); + + private: + friend ShaderCache *get_shader_cache(id<MTLDevice> mtlDevice); + + void compile_thread_func(int thread_index); + + using PipelineCollection = std::vector<unique_ptr<MetalKernelPipeline>>; + + struct PipelineRequest { + MetalKernelPipeline *pipeline = nullptr; + std::function<void(MetalKernelPipeline *)> completionHandler; + }; + + std::mutex cache_mutex; + + PipelineCollection pipelines[DEVICE_KERNEL_NUM]; + id<MTLDevice> mtlDevice; + + bool running = false; + std::condition_variable cond_var; + std::deque<PipelineRequest> request_queue; + std::vector<std::thread> compile_threads; + std::atomic_int incomplete_requests = 0; +}; + +std::mutex g_shaderCacheMutex; +std::map<id<MTLDevice>, unique_ptr<ShaderCache>> g_shaderCache; + +ShaderCache *get_shader_cache(id<MTLDevice> mtlDevice) +{ + thread_scoped_lock lock(g_shaderCacheMutex); + auto it = g_shaderCache.find(mtlDevice); + if (it != g_shaderCache.end()) { + return it->second.get(); + } + + g_shaderCache[mtlDevice] = make_unique<ShaderCache>(mtlDevice); + return g_shaderCache[mtlDevice].get(); +} + +ShaderCache::~ShaderCache() +{ + metal_printf("ShaderCache shutting down with incomplete_requests = %d\n", + int(incomplete_requests)); + + running = false; + cond_var.notify_all(); + for (auto &thread : compile_threads) { + thread.join(); + } +} + +void ShaderCache::wait_for_all() +{ + while (incomplete_requests > 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } +} + +void ShaderCache::compile_thread_func(int thread_index) +{ + while (1) { + + /* wait for / acquire next request */ + PipelineRequest request; + { + thread_scoped_lock lock(cache_mutex); + cond_var.wait(lock, [&] { return !running || !request_queue.empty(); }); + if (!running) { + break; + } + + if (!request_queue.empty()) { + request = request_queue.front(); + request_queue.pop_front(); + } + } + + /* service request */ + if (request.pipeline) { + request.pipeline->compile(); + incomplete_requests--; + } } } -bool MetalDeviceKernel::load(MetalDevice *device, - MetalKernelLoadDesc const &desc_in, - MD5Hash const &md5) +void ShaderCache::load_kernel(DeviceKernel device_kernel, + MetalDevice *device, + bool scene_specialized) { - __block MetalKernelLoadDesc const desc(desc_in); - if (desc.kernel_index == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { + { + /* create compiler threads on first run */ + thread_scoped_lock lock(cache_mutex); + if (compile_threads.empty()) { + running = true; + for (int i = 0; i < max_mtlcompiler_threads; i++) { + compile_threads.push_back(std::thread([&] { compile_thread_func(i); })); + } + } + } + + if (device_kernel == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { /* skip megakernel */ - return true; + return; } - bool use_binary_archive = true; - if (device->device_vendor == METAL_GPU_APPLE) { - /* Workaround for T94142: Cycles Metal crash with simultaneous viewport and final render */ - use_binary_archive = false; + if (scene_specialized) { + /* Only specialize kernels where it can make an impact. */ + if (device_kernel < DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST || + device_kernel > DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { + return; + } } - if (auto str = getenv("CYCLES_METAL_DISABLE_BINARY_ARCHIVES")) { - use_binary_archive = (atoi(str) == 0); + { + /* check whether the kernel has already been requested / cached */ + thread_scoped_lock lock(cache_mutex); + for (auto &pipeline : pipelines[device_kernel]) { + if (scene_specialized) { + if (pipeline->source_md5 == device->source_md5[PSO_SPECIALISED]) { + /* we already requested a pipeline that is specialized for this kernel data */ + metal_printf("Specialized kernel already requested (%s)\n", + device_kernel_as_string(device_kernel)); + return; + } + } + else { + if (pipeline->source_md5 == device->source_md5[PSO_GENERIC]) { + /* we already requested a generic pipeline for this kernel */ + metal_printf("Generic kernel already requested (%s)\n", + device_kernel_as_string(device_kernel)); + return; + } + } + } } - id<MTLBinaryArchive> archive = nil; - string metalbin_path; - if (use_binary_archive) { - NSProcessInfo *processInfo = [NSProcessInfo processInfo]; - string osVersion = [[processInfo operatingSystemVersionString] UTF8String]; - MD5Hash local_md5(md5); - local_md5.append(osVersion); - string metalbin_name = string(desc.function_name) + "." + local_md5.get_hex() + - to_string(desc.pso_index) + ".bin"; - metalbin_path = path_cache_get(path_join("kernels", metalbin_name)); - path_create_directories(metalbin_path); + incomplete_requests++; + + PipelineRequest request; + request.pipeline = new MetalKernelPipeline; + request.pipeline->scene_specialized = scene_specialized; + request.pipeline->mtlDevice = mtlDevice; + request.pipeline->source_md5 = + device->source_md5[scene_specialized ? PSO_SPECIALISED : PSO_GENERIC]; + request.pipeline->mtlLibrary = + device->mtlLibrary[scene_specialized ? PSO_SPECIALISED : PSO_GENERIC]; + request.pipeline->device_kernel = device_kernel; + request.pipeline->threads_per_threadgroup = device->max_threads_per_threadgroup; + + /* metalrt options */ + request.pipeline->use_metalrt = device->use_metalrt; + request.pipeline->metalrt_hair = device->use_metalrt && + (device->kernel_features & KERNEL_FEATURE_HAIR); + request.pipeline->metalrt_hair_thick = device->use_metalrt && + (device->kernel_features & KERNEL_FEATURE_HAIR_THICK); + request.pipeline->metalrt_pointcloud = device->use_metalrt && + (device->kernel_features & KERNEL_FEATURE_POINTCLOUD); + + { + thread_scoped_lock lock(cache_mutex); + pipelines[device_kernel].push_back(unique_ptr<MetalKernelPipeline>(request.pipeline)); + request_queue.push_back(request); + } + cond_var.notify_one(); +} - if (path_exists(metalbin_path) && use_binary_archive) { - if (@available(macOS 11.0, *)) { - MTLBinaryArchiveDescriptor *archiveDesc = [[MTLBinaryArchiveDescriptor alloc] init]; - archiveDesc.url = [NSURL fileURLWithPath:@(metalbin_path.c_str())]; - archive = [device->mtlDevice newBinaryArchiveWithDescriptor:archiveDesc error:nil]; - [archiveDesc release]; +MetalKernelPipeline *ShaderCache::get_best_pipeline(DeviceKernel kernel, const MetalDevice *device) +{ + thread_scoped_lock lock(cache_mutex); + auto &collection = pipelines[kernel]; + if (collection.empty()) { + return nullptr; + } + + /* metalrt options */ + bool use_metalrt = device->use_metalrt; + bool metalrt_hair = use_metalrt && (device->kernel_features & KERNEL_FEATURE_HAIR); + bool metalrt_hair_thick = use_metalrt && (device->kernel_features & KERNEL_FEATURE_HAIR_THICK); + bool metalrt_pointcloud = use_metalrt && (device->kernel_features & KERNEL_FEATURE_POINTCLOUD); + + MetalKernelPipeline *best_pipeline = nullptr; + for (auto &pipeline : collection) { + if (!pipeline->loaded) { + /* still loading - ignore */ + continue; + } + + if (pipeline->use_metalrt != use_metalrt || pipeline->metalrt_hair != metalrt_hair || + pipeline->metalrt_hair_thick != metalrt_hair_thick || + pipeline->metalrt_pointcloud != metalrt_pointcloud) { + /* wrong combination of metalrt options */ + continue; + } + + if (pipeline->scene_specialized) { + if (pipeline->source_md5 == device->source_md5[PSO_SPECIALISED]) { + best_pipeline = pipeline.get(); } } + else if (!best_pipeline) { + best_pipeline = pipeline.get(); + } + } + + return best_pipeline; +} + +void MetalKernelPipeline::compile() +{ + int pso_type = scene_specialized ? PSO_SPECIALISED : PSO_GENERIC; + + const std::string function_name = std::string("cycles_metal_") + + device_kernel_as_string(device_kernel); + + int threads_per_threadgroup = this->threads_per_threadgroup; + if (device_kernel > DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL && + device_kernel < DEVICE_KERNEL_INTEGRATOR_RESET) { + /* Always use 512 for the sorting kernels */ + threads_per_threadgroup = 512; } - NSString *entryPoint = [@(desc.function_name) copy]; + NSString *entryPoint = [@(function_name.c_str()) copy]; NSError *error = NULL; if (@available(macOS 11.0, *)) { MTLFunctionDescriptor *func_desc = [MTLIntersectionFunctionDescriptor functionDescriptor]; func_desc.name = entryPoint; - if (desc.constant_values) { - func_desc.constantValues = desc.constant_values; - } - pso[desc.pso_index].function = [device->mtlLibrary[desc.pso_index] - newFunctionWithDescriptor:func_desc - error:&error]; + function = [mtlLibrary newFunctionWithDescriptor:func_desc error:&error]; } + [entryPoint release]; - if (pso[desc.pso_index].function == nil) { + if (function == nil) { NSString *err = [error localizedDescription]; string errors = [err UTF8String]; + metal_printf("Error getting function \"%s\": %s", function_name.c_str(), errors.c_str()); + return; + } + + function.label = [entryPoint copy]; + + if (use_metalrt) { + if (@available(macOS 11.0, *)) { + /* create the id<MTLFunction> for each intersection function */ + const char *function_names[] = { + "__anyhit__cycles_metalrt_visibility_test_tri", + "__anyhit__cycles_metalrt_visibility_test_box", + "__anyhit__cycles_metalrt_shadow_all_hit_tri", + "__anyhit__cycles_metalrt_shadow_all_hit_box", + "__anyhit__cycles_metalrt_local_hit_tri", + "__anyhit__cycles_metalrt_local_hit_box", + "__intersection__curve_ribbon", + "__intersection__curve_ribbon_shadow", + "__intersection__curve_all", + "__intersection__curve_all_shadow", + "__intersection__point", + "__intersection__point_shadow", + }; + assert(sizeof(function_names) / sizeof(function_names[0]) == METALRT_FUNC_NUM); + + MTLFunctionDescriptor *desc = [MTLIntersectionFunctionDescriptor functionDescriptor]; + for (int i = 0; i < METALRT_FUNC_NUM; i++) { + const char *function_name = function_names[i]; + desc.name = [@(function_name) copy]; + + NSError *error = NULL; + rt_intersection_function[i] = [mtlLibrary newFunctionWithDescriptor:desc error:&error]; - device->set_error( - string_printf("Error getting function \"%s\": %s", desc.function_name, errors.c_str())); - return false; + if (rt_intersection_function[i] == nil) { + NSString *err = [error localizedDescription]; + string errors = [err UTF8String]; + + error_str = string_printf( + "Error getting intersection function \"%s\": %s", function_name, errors.c_str()); + break; + } + + rt_intersection_function[i].label = [@(function_name) copy]; + } + } } - pso[desc.pso_index].function.label = [@(desc.function_name) copy]; + NSArray *table_functions[METALRT_TABLE_NUM] = {nil}; + NSArray *linked_functions = nil; + + if (use_metalrt) { + id<MTLFunction> curve_intersect_default = nil; + id<MTLFunction> curve_intersect_shadow = nil; + id<MTLFunction> point_intersect_default = nil; + id<MTLFunction> point_intersect_shadow = nil; + if (metalrt_hair) { + /* Add curve intersection programs. */ + if (metalrt_hair_thick) { + /* Slower programs for thick hair since that also slows down ribbons. + * Ideally this should not be needed. */ + curve_intersect_default = rt_intersection_function[METALRT_FUNC_CURVE_ALL]; + curve_intersect_shadow = rt_intersection_function[METALRT_FUNC_CURVE_ALL_SHADOW]; + } + else { + curve_intersect_default = rt_intersection_function[METALRT_FUNC_CURVE_RIBBON]; + curve_intersect_shadow = rt_intersection_function[METALRT_FUNC_CURVE_RIBBON_SHADOW]; + } + } + if (metalrt_pointcloud) { + point_intersect_default = rt_intersection_function[METALRT_FUNC_POINT]; + point_intersect_shadow = rt_intersection_function[METALRT_FUNC_POINT_SHADOW]; + } + table_functions[METALRT_TABLE_DEFAULT] = [NSArray + arrayWithObjects:rt_intersection_function[METALRT_FUNC_DEFAULT_TRI], + curve_intersect_default ? + curve_intersect_default : + rt_intersection_function[METALRT_FUNC_DEFAULT_BOX], + point_intersect_default ? + point_intersect_default : + rt_intersection_function[METALRT_FUNC_DEFAULT_BOX], + nil]; + table_functions[METALRT_TABLE_SHADOW] = [NSArray + arrayWithObjects:rt_intersection_function[METALRT_FUNC_SHADOW_TRI], + curve_intersect_shadow ? + curve_intersect_shadow : + rt_intersection_function[METALRT_FUNC_SHADOW_BOX], + point_intersect_shadow ? + point_intersect_shadow : + rt_intersection_function[METALRT_FUNC_SHADOW_BOX], + nil]; + table_functions[METALRT_TABLE_LOCAL] = [NSArray + arrayWithObjects:rt_intersection_function[METALRT_FUNC_LOCAL_TRI], + rt_intersection_function[METALRT_FUNC_LOCAL_BOX], + rt_intersection_function[METALRT_FUNC_LOCAL_BOX], + nil]; + + NSMutableSet *unique_functions = [NSMutableSet + setWithArray:table_functions[METALRT_TABLE_DEFAULT]]; + [unique_functions addObjectsFromArray:table_functions[METALRT_TABLE_SHADOW]]; + [unique_functions addObjectsFromArray:table_functions[METALRT_TABLE_LOCAL]]; + + if (kernel_has_intersection(device_kernel)) { + linked_functions = [[NSArray arrayWithArray:[unique_functions allObjects]] + sortedArrayUsingComparator:^NSComparisonResult(id<MTLFunction> f1, id<MTLFunction> f2) { + return [f1.label compare:f2.label]; + }]; + } + unique_functions = nil; + } - __block MTLComputePipelineDescriptor *computePipelineStateDescriptor = + MTLComputePipelineDescriptor *computePipelineStateDescriptor = [[MTLComputePipelineDescriptor alloc] init]; computePipelineStateDescriptor.buffers[0].mutability = MTLMutabilityImmutable; @@ -111,52 +406,86 @@ bool MetalDeviceKernel::load(MetalDevice *device, computePipelineStateDescriptor.buffers[2].mutability = MTLMutabilityImmutable; if (@available(macos 10.14, *)) { - computePipelineStateDescriptor.maxTotalThreadsPerThreadgroup = desc.threads_per_threadgroup; + computePipelineStateDescriptor.maxTotalThreadsPerThreadgroup = threads_per_threadgroup; } computePipelineStateDescriptor.threadGroupSizeIsMultipleOfThreadExecutionWidth = true; - computePipelineStateDescriptor.computeFunction = pso[desc.pso_index].function; + computePipelineStateDescriptor.computeFunction = function; + if (@available(macOS 11.0, *)) { /* Attach the additional functions to an MTLLinkedFunctions object */ - if (desc.linked_functions) { + if (linked_functions) { computePipelineStateDescriptor.linkedFunctions = [[MTLLinkedFunctions alloc] init]; - computePipelineStateDescriptor.linkedFunctions.functions = desc.linked_functions; + computePipelineStateDescriptor.linkedFunctions.functions = linked_functions; } - computePipelineStateDescriptor.maxCallStackDepth = 1; + if (use_metalrt) { + computePipelineStateDescriptor.maxCallStackDepth = 8; + } } - /* Create a new Compute pipeline state object */ MTLPipelineOption pipelineOptions = MTLPipelineOptionNone; - bool creating_new_archive = false; + bool use_binary_archive = true; + if (auto str = getenv("CYCLES_METAL_DISABLE_BINARY_ARCHIVES")) { + use_binary_archive = (atoi(str) == 0); + } + + id<MTLBinaryArchive> archive = nil; + string metalbin_path; + string metalbin_name; + if (use_binary_archive) { + NSProcessInfo *processInfo = [NSProcessInfo processInfo]; + string osVersion = [[processInfo operatingSystemVersionString] UTF8String]; + MD5Hash local_md5; + local_md5.append(source_md5); + local_md5.append(osVersion); + local_md5.append((uint8_t *)&this->threads_per_threadgroup, + sizeof(this->threads_per_threadgroup)); + + string options; + if (use_metalrt && kernel_has_intersection(device_kernel)) { + /* incorporate any MetalRT specializations into the archive name */ + options += string_printf(".hair_%d.hair_thick_%d.pointcloud_%d", + metalrt_hair ? 1 : 0, + metalrt_hair_thick ? 1 : 0, + metalrt_pointcloud ? 1 : 0); + } + + /* Replace non-alphanumerical characters with underscores. */ + string device_name = [mtlDevice.name UTF8String]; + for (char &c : device_name) { + if ((c < '0' || c > '9') && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z')) { + c = '_'; + } + } + + metalbin_name = device_name; + metalbin_name = path_join(metalbin_name, device_kernel_as_string(device_kernel)); + metalbin_name = path_join(metalbin_name, kernel_type_as_string(pso_type)); + metalbin_name = path_join(metalbin_name, local_md5.get_hex() + options + ".bin"); + + metalbin_path = path_cache_get(path_join("kernels", metalbin_name)); + path_create_directories(metalbin_path); + + if (path_exists(metalbin_path) && use_binary_archive) { + if (@available(macOS 11.0, *)) { + MTLBinaryArchiveDescriptor *archiveDesc = [[MTLBinaryArchiveDescriptor alloc] init]; + archiveDesc.url = [NSURL fileURLWithPath:@(metalbin_path.c_str())]; + archive = [mtlDevice newBinaryArchiveWithDescriptor:archiveDesc error:nil]; + [archiveDesc release]; + } + } + } + + __block bool creating_new_archive = false; if (@available(macOS 11.0, *)) { if (use_binary_archive) { if (!archive) { MTLBinaryArchiveDescriptor *archiveDesc = [[MTLBinaryArchiveDescriptor alloc] init]; archiveDesc.url = nil; - archive = [device->mtlDevice newBinaryArchiveWithDescriptor:archiveDesc error:nil]; + archive = [mtlDevice newBinaryArchiveWithDescriptor:archiveDesc error:nil]; creating_new_archive = true; - - double starttime = time_dt(); - - if (![archive addComputePipelineFunctionsWithDescriptor:computePipelineStateDescriptor - error:&error]) { - NSString *errStr = [error localizedDescription]; - metal_printf("Failed to add PSO to archive:\n%s\n", - errStr ? [errStr UTF8String] : "nil"); - } - else { - double duration = time_dt() - starttime; - metal_printf("%2d | %-55s | %7.2fs\n", - desc.kernel_index, - device_kernel_as_string((DeviceKernel)desc.kernel_index), - duration); - - if (desc.pso_index == PSO_GENERIC) { - this->load_duration = duration; - } - } } computePipelineStateDescriptor.binaryArchives = [NSArray arrayWithObjects:archive, nil]; pipelineOptions = MTLPipelineOptionFailOnBinaryArchiveMiss; @@ -170,17 +499,14 @@ bool MetalDeviceKernel::load(MetalDevice *device, MTLComputePipelineReflection *reflection, NSError *error) { bool recreate_archive = false; - if (computePipelineState == nil && archive && !creating_new_archive) { - - assert(0); - + if (computePipelineState == nil && archive) { NSString *errStr = [error localizedDescription]; metal_printf( "Failed to create compute pipeline state \"%s\" from archive - attempting recreation... " "(error: %s)\n", - device_kernel_as_string((DeviceKernel)desc.kernel_index), + device_kernel_as_string((DeviceKernel)device_kernel), errStr ? [errStr UTF8String] : "nil"); - computePipelineState = [device->mtlDevice + computePipelineState = [mtlDevice newComputePipelineStateWithDescriptor:computePipelineStateDescriptor options:MTLPipelineOptionNone reflection:nullptr @@ -192,32 +518,23 @@ bool MetalDeviceKernel::load(MetalDevice *device, if (computePipelineState == nil) { NSString *errStr = [error localizedDescription]; - device->set_error(string_printf("Failed to create compute pipeline state \"%s\", error: \n", - device_kernel_as_string((DeviceKernel)desc.kernel_index)) + - (errStr ? [errStr UTF8String] : "nil")); - metal_printf("%2d | %-55s | %7.2fs | FAILED!\n", - desc.kernel_index, - device_kernel_as_string((DeviceKernel)desc.kernel_index), + error_str = string_printf("Failed to create compute pipeline state \"%s\", error: \n", + device_kernel_as_string((DeviceKernel)device_kernel)); + error_str += (errStr ? [errStr UTF8String] : "nil"); + metal_printf("%16s | %2d | %-55s | %7.2fs | FAILED!\n", + kernel_type_as_string(pso_type), + device_kernel, + device_kernel_as_string((DeviceKernel)device_kernel), duration); return; } - pso[desc.pso_index].pipeline = computePipelineState; - num_threads_per_block = round_down(computePipelineState.maxTotalThreadsPerThreadgroup, - computePipelineState.threadExecutionWidth); + int num_threads_per_block = round_down(computePipelineState.maxTotalThreadsPerThreadgroup, + computePipelineState.threadExecutionWidth); num_threads_per_block = std::max(num_threads_per_block, (int)computePipelineState.threadExecutionWidth); - - if (!use_binary_archive) { - metal_printf("%2d | %-55s | %7.2fs\n", - desc.kernel_index, - device_kernel_as_string((DeviceKernel)desc.kernel_index), - duration); - - if (desc.pso_index == PSO_GENERIC) { - this->load_duration = duration; - } - } + this->pipeline = computePipelineState; + this->num_threads_per_block = num_threads_per_block; if (@available(macOS 11.0, *)) { if (creating_new_archive || recreate_archive) { @@ -228,304 +545,85 @@ bool MetalDeviceKernel::load(MetalDevice *device, } } } + }; - [computePipelineStateDescriptor release]; - computePipelineStateDescriptor = nil; - - if (device->use_metalrt && desc.linked_functions) { - for (int table = 0; table < METALRT_TABLE_NUM; table++) { - if (@available(macOS 11.0, *)) { - MTLIntersectionFunctionTableDescriptor *ift_desc = - [[MTLIntersectionFunctionTableDescriptor alloc] init]; - ift_desc.functionCount = desc.intersector_functions[table].count; - - pso[desc.pso_index].intersection_func_table[table] = [pso[desc.pso_index].pipeline - newIntersectionFunctionTableWithDescriptor:ift_desc]; - - /* Finally write the function handles into this pipeline's table */ - for (int i = 0; i < 2; i++) { - id<MTLFunctionHandle> handle = [pso[desc.pso_index].pipeline - functionHandleWithFunction:desc.intersector_functions[table][i]]; - [pso[desc.pso_index].intersection_func_table[table] setFunction:handle atIndex:i]; - } + /* Block on load to ensure we continue with a valid kernel function */ + if (creating_new_archive) { + starttime = time_dt(); + NSError *error; + if (![archive addComputePipelineFunctionsWithDescriptor:computePipelineStateDescriptor + error:&error]) { + NSString *errStr = [error localizedDescription]; + metal_printf("Failed to add PSO to archive:\n%s\n", errStr ? [errStr UTF8String] : "nil"); + } + } + id<MTLComputePipelineState> pipeline = [mtlDevice + newComputePipelineStateWithDescriptor:computePipelineStateDescriptor + options:pipelineOptions + reflection:nullptr + error:&error]; + completionHandler(pipeline, nullptr, error); + + this->loaded = true; + [computePipelineStateDescriptor release]; + computePipelineStateDescriptor = nil; + + if (use_metalrt && linked_functions) { + for (int table = 0; table < METALRT_TABLE_NUM; table++) { + if (@available(macOS 11.0, *)) { + MTLIntersectionFunctionTableDescriptor *ift_desc = + [[MTLIntersectionFunctionTableDescriptor alloc] init]; + ift_desc.functionCount = table_functions[table].count; + intersection_func_table[table] = [this->pipeline + newIntersectionFunctionTableWithDescriptor:ift_desc]; + + /* Finally write the function handles into this pipeline's table */ + for (int i = 0; i < 2; i++) { + id<MTLFunctionHandle> handle = [pipeline + functionHandleWithFunction:table_functions[table][i]]; + [intersection_func_table[table] setFunction:handle atIndex:i]; } } } + } - mark_loaded(desc.pso_index); - }; + double duration = time_dt() - starttime; - if (desc.pso_index == PSO_SPECIALISED) { - /* Asynchronous load */ - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSError *error; - id<MTLComputePipelineState> pipeline = [device->mtlDevice - newComputePipelineStateWithDescriptor:computePipelineStateDescriptor - options:pipelineOptions - reflection:nullptr - error:&error]; - completionHandler(pipeline, nullptr, error); - }); + if (!use_binary_archive) { + metal_printf("%16s | %2d | %-55s | %7.2fs\n", + kernel_type_as_string(pso_type), + int(device_kernel), + device_kernel_as_string(device_kernel), + duration); } else { - /* Block on load to ensure we continue with a valid kernel function */ - id<MTLComputePipelineState> pipeline = [device->mtlDevice - newComputePipelineStateWithDescriptor:computePipelineStateDescriptor - options:pipelineOptions - reflection:nullptr - error:&error]; - completionHandler(pipeline, nullptr, error); - } - - return true; -} - -const MetalKernelPipeline &MetalDeviceKernel::get_pso() const -{ - if (pso[PSO_SPECIALISED].loaded) { - return pso[PSO_SPECIALISED]; + metal_printf("%16s | %2d | %-55s | %7.2fs | %s: %s\n", + kernel_type_as_string(pso_type), + device_kernel, + device_kernel_as_string((DeviceKernel)device_kernel), + duration, + creating_new_archive ? " new" : "load", + metalbin_name.c_str()); } - - assert(pso[PSO_GENERIC].loaded); - return pso[PSO_GENERIC]; } -bool MetalDeviceKernels::load(MetalDevice *device, int kernel_type) +bool MetalDeviceKernels::load(MetalDevice *device, bool scene_specialized) { - bool any_error = false; - - MD5Hash md5; - - /* Build the function constant table */ - MTLFunctionConstantValues *constant_values = nullptr; - if (kernel_type == PSO_SPECIALISED) { - constant_values = [MTLFunctionConstantValues new]; - -# define KERNEL_FILM(_type, name) \ - [constant_values setConstantValue:&data.film.name \ - type:get_MTLDataType_##_type() \ - atIndex:KernelData_film_##name]; \ - md5.append((uint8_t *)&data.film.name, sizeof(data.film.name)); - -# define KERNEL_BACKGROUND(_type, name) \ - [constant_values setConstantValue:&data.background.name \ - type:get_MTLDataType_##_type() \ - atIndex:KernelData_background_##name]; \ - md5.append((uint8_t *)&data.background.name, sizeof(data.background.name)); - -# define KERNEL_INTEGRATOR(_type, name) \ - [constant_values setConstantValue:&data.integrator.name \ - type:get_MTLDataType_##_type() \ - atIndex:KernelData_integrator_##name]; \ - md5.append((uint8_t *)&data.integrator.name, sizeof(data.integrator.name)); - -# define KERNEL_BVH(_type, name) \ - [constant_values setConstantValue:&data.bvh.name \ - type:get_MTLDataType_##_type() \ - atIndex:KernelData_bvh_##name]; \ - md5.append((uint8_t *)&data.bvh.name, sizeof(data.bvh.name)); - - /* METAL_WIP: populate constant_values based on KernelData */ - assert(0); - /* - const KernelData &data = device->launch_params.data; - # include "kernel/types/background.h" - # include "kernel/types/bvh.h" - # include "kernel/types/film.h" - # include "kernel/types/integrator.h" - */ - } - - if (device->use_metalrt) { - if (@available(macOS 11.0, *)) { - /* create the id<MTLFunction> for each intersection function */ - const char *function_names[] = { - "__anyhit__cycles_metalrt_visibility_test_tri", - "__anyhit__cycles_metalrt_visibility_test_box", - "__anyhit__cycles_metalrt_shadow_all_hit_tri", - "__anyhit__cycles_metalrt_shadow_all_hit_box", - "__anyhit__cycles_metalrt_local_hit_tri", - "__anyhit__cycles_metalrt_local_hit_box", - "__intersection__curve_ribbon", - "__intersection__curve_ribbon_shadow", - "__intersection__curve_all", - "__intersection__curve_all_shadow", - "__intersection__point", - "__intersection__point_shadow", - }; - assert(sizeof(function_names) / sizeof(function_names[0]) == METALRT_FUNC_NUM); - - MTLFunctionDescriptor *desc = [MTLIntersectionFunctionDescriptor functionDescriptor]; - if (kernel_type == PSO_SPECIALISED) { - desc.constantValues = constant_values; - } - for (int i = 0; i < METALRT_FUNC_NUM; i++) { - const char *function_name = function_names[i]; - desc.name = [@(function_name) copy]; - - NSError *error = NULL; - rt_intersection_funcs[kernel_type][i] = [device->mtlLibrary[kernel_type] - newFunctionWithDescriptor:desc - error:&error]; - - if (rt_intersection_funcs[kernel_type][i] == nil) { - NSString *err = [error localizedDescription]; - string errors = [err UTF8String]; - - device->set_error(string_printf( - "Error getting intersection function \"%s\": %s", function_name, errors.c_str())); - any_error = true; - break; - } - - rt_intersection_funcs[kernel_type][i].label = [@(function_name) copy]; - } - } - } - md5.append(device->source_used_for_compile[kernel_type]); - - string hash = md5.get_hex(); - if (loaded_md5[kernel_type] == hash) { - return true; - } - - if (!any_error) { - NSArray *table_functions[METALRT_TABLE_NUM] = {nil}; - NSArray *function_list = nil; - - if (device->use_metalrt) { - id<MTLFunction> curve_intersect_default = nil; - id<MTLFunction> curve_intersect_shadow = nil; - id<MTLFunction> point_intersect_default = nil; - id<MTLFunction> point_intersect_shadow = nil; - if (device->kernel_features & KERNEL_FEATURE_HAIR) { - /* Add curve intersection programs. */ - if (device->kernel_features & KERNEL_FEATURE_HAIR_THICK) { - /* Slower programs for thick hair since that also slows down ribbons. - * Ideally this should not be needed. */ - curve_intersect_default = rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_ALL]; - curve_intersect_shadow = - rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_ALL_SHADOW]; - } - else { - curve_intersect_default = rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_RIBBON]; - curve_intersect_shadow = - rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_RIBBON_SHADOW]; - } - } - if (device->kernel_features & KERNEL_FEATURE_POINTCLOUD) { - point_intersect_default = rt_intersection_funcs[kernel_type][METALRT_FUNC_POINT]; - point_intersect_shadow = rt_intersection_funcs[kernel_type][METALRT_FUNC_POINT_SHADOW]; - } - table_functions[METALRT_TABLE_DEFAULT] = [NSArray - arrayWithObjects:rt_intersection_funcs[kernel_type][METALRT_FUNC_DEFAULT_TRI], - curve_intersect_default ? - curve_intersect_default : - rt_intersection_funcs[kernel_type][METALRT_FUNC_DEFAULT_BOX], - point_intersect_default ? - point_intersect_default : - rt_intersection_funcs[kernel_type][METALRT_FUNC_DEFAULT_BOX], - nil]; - table_functions[METALRT_TABLE_SHADOW] = [NSArray - arrayWithObjects:rt_intersection_funcs[kernel_type][METALRT_FUNC_SHADOW_TRI], - curve_intersect_shadow ? - curve_intersect_shadow : - rt_intersection_funcs[kernel_type][METALRT_FUNC_SHADOW_BOX], - point_intersect_shadow ? - point_intersect_shadow : - rt_intersection_funcs[kernel_type][METALRT_FUNC_SHADOW_BOX], - nil]; - table_functions[METALRT_TABLE_LOCAL] = [NSArray - arrayWithObjects:rt_intersection_funcs[kernel_type][METALRT_FUNC_LOCAL_TRI], - rt_intersection_funcs[kernel_type][METALRT_FUNC_LOCAL_BOX], - rt_intersection_funcs[kernel_type][METALRT_FUNC_LOCAL_BOX], - nil]; - - NSMutableSet *unique_functions = [NSMutableSet - setWithArray:table_functions[METALRT_TABLE_DEFAULT]]; - [unique_functions addObjectsFromArray:table_functions[METALRT_TABLE_SHADOW]]; - [unique_functions addObjectsFromArray:table_functions[METALRT_TABLE_LOCAL]]; - - function_list = [[NSArray arrayWithArray:[unique_functions allObjects]] - sortedArrayUsingComparator:^NSComparisonResult(id<MTLFunction> f1, id<MTLFunction> f2) { - return [f1.label compare:f2.label]; - }]; - - unique_functions = nil; - } - - metal_printf("Starting %s \"cycles_metal_...\" pipeline builds\n", - kernel_type_as_string(kernel_type)); - - tbb::task_arena local_arena(max_mtlcompiler_threads); - local_arena.execute([&]() { - parallel_for(int(0), int(DEVICE_KERNEL_NUM), [&](int i) { - /* skip megakernel */ - if (i == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { - return; - } - - /* Only specialize kernels where it can make an impact. */ - if (kernel_type == PSO_SPECIALISED) { - if (i < DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST || - i > DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { - return; - } - } - - MetalDeviceKernel &kernel = kernels_[i]; - - const std::string function_name = std::string("cycles_metal_") + - device_kernel_as_string((DeviceKernel)i); - int threads_per_threadgroup = device->max_threads_per_threadgroup; - if (i > DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL && i < DEVICE_KERNEL_INTEGRATOR_RESET) { - /* Always use 512 for the sorting kernels */ - threads_per_threadgroup = 512; - } - - NSArray *kernel_function_list = nil; - - if (i == DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST || - i == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW || - i == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE || - i == DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK || - i == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE) { - kernel_function_list = function_list; - } - - MetalKernelLoadDesc desc; - desc.pso_index = kernel_type; - desc.kernel_index = i; - desc.linked_functions = kernel_function_list; - desc.intersector_functions.defaults = table_functions[METALRT_TABLE_DEFAULT]; - desc.intersector_functions.shadow = table_functions[METALRT_TABLE_SHADOW]; - desc.intersector_functions.local = table_functions[METALRT_TABLE_LOCAL]; - desc.constant_values = constant_values; - desc.threads_per_threadgroup = threads_per_threadgroup; - desc.function_name = function_name.c_str(); - - bool success = kernel.load(device, desc, md5); - - any_error |= !success; - }); - }); + auto shader_cache = get_shader_cache(device->mtlDevice); + for (int i = 0; i < DEVICE_KERNEL_NUM; i++) { + shader_cache->load_kernel((DeviceKernel)i, device, scene_specialized); } - bool loaded = !any_error; - if (loaded) { - loaded_md5[kernel_type] = hash; + if (!scene_specialized || getenv("CYCLES_METAL_PROFILING")) { + shader_cache->wait_for_all(); } - return loaded; -} - -const MetalDeviceKernel &MetalDeviceKernels::get(DeviceKernel kernel) const -{ - return kernels_[(int)kernel]; + return true; } -bool MetalDeviceKernels::available(DeviceKernel kernel) const +const MetalKernelPipeline *MetalDeviceKernels::get_best_pipeline(const MetalDevice *device, + DeviceKernel kernel) { - return kernels_[(int)kernel].get_pso().function != nil; + return get_shader_cache(device->mtlDevice)->get_best_pipeline(kernel, device); } CCL_NAMESPACE_END diff --git a/intern/cycles/device/metal/queue.mm b/intern/cycles/device/metal/queue.mm index 1686ab95ffa..ec10e091b25 100644 --- a/intern/cycles/device/metal/queue.mm +++ b/intern/cycles/device/metal/queue.mm @@ -108,9 +108,6 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, VLOG(3) << "Metal queue launch " << device_kernel_as_string(kernel) << ", work_size " << work_size; - const MetalDeviceKernel &metal_kernel = metal_device->kernels.get(kernel); - const MetalKernelPipeline &metal_kernel_pso = metal_kernel.get_pso(); - id<MTLComputeCommandEncoder> mtlComputeCommandEncoder = get_compute_encoder(kernel); /* Determine size requirement for argument buffer. */ @@ -212,6 +209,14 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, } bytes_written = globals_offsets + sizeof(KernelParamsMetal); + const MetalKernelPipeline *metal_kernel_pso = MetalDeviceKernels::get_best_pipeline(metal_device, + kernel); + if (!metal_kernel_pso) { + metal_device->set_error( + string_printf("No MetalKernelPipeline for %s\n", device_kernel_as_string(kernel))); + return false; + } + /* Encode ancillaries */ [metal_device->mtlAncillaryArgEncoder setArgumentBuffer:arg_buffer offset:metal_offsets]; [metal_device->mtlAncillaryArgEncoder setBuffer:metal_device->texture_bindings_2d @@ -228,14 +233,14 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, } for (int table = 0; table < METALRT_TABLE_NUM; table++) { - if (metal_kernel_pso.intersection_func_table[table]) { - [metal_kernel_pso.intersection_func_table[table] setBuffer:arg_buffer - offset:globals_offsets - atIndex:1]; + if (metal_kernel_pso->intersection_func_table[table]) { + [metal_kernel_pso->intersection_func_table[table] setBuffer:arg_buffer + offset:globals_offsets + atIndex:1]; [metal_device->mtlAncillaryArgEncoder - setIntersectionFunctionTable:metal_kernel_pso.intersection_func_table[table] + setIntersectionFunctionTable:metal_kernel_pso->intersection_func_table[table] atIndex:3 + table]; - [mtlComputeCommandEncoder useResource:metal_kernel_pso.intersection_func_table[table] + [mtlComputeCommandEncoder useResource:metal_kernel_pso->intersection_func_table[table] usage:MTLResourceUsageRead]; } else { @@ -281,10 +286,10 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, } } - [mtlComputeCommandEncoder setComputePipelineState:metal_kernel_pso.pipeline]; + [mtlComputeCommandEncoder setComputePipelineState:metal_kernel_pso->pipeline]; /* Compute kernel launch parameters. */ - const int num_threads_per_block = metal_kernel.get_num_threads_per_block(); + const int num_threads_per_block = metal_kernel_pso->num_threads_per_block; int shared_mem_bytes = 0; @@ -314,7 +319,7 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, threadsPerThreadgroup:size_threads_per_threadgroup]; [mtlCommandBuffer addCompletedHandler:^(id<MTLCommandBuffer> command_buffer) { - NSString *kernel_name = metal_kernel_pso.function.label; + NSString *kernel_name = metal_kernel_pso->function.label; /* Enhanced command buffer errors are only available in 11.0+ */ if (@available(macos 11.0, *)) { @@ -547,6 +552,8 @@ id<MTLComputeCommandEncoder> MetalDeviceQueue::get_compute_encoder(DeviceKernel computeCommandEncoderWithDispatchType:concurrent ? MTLDispatchTypeConcurrent : MTLDispatchTypeSerial]; + [mtlComputeEncoder setLabel:@(device_kernel_as_string(kernel))]; + /* declare usage of MTLBuffers etc */ prepare_resources(kernel); } diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp index 8830d8c44ac..9fc265bc327 100644 --- a/intern/cycles/device/optix/device_impl.cpp +++ b/intern/cycles/device/optix/device_impl.cpp @@ -23,6 +23,7 @@ # include "util/md5.h" # include "util/path.h" # include "util/progress.h" +# include "util/task.h" # include "util/time.h" # undef __KERNEL_CPU__ @@ -216,6 +217,25 @@ static OptixResult optixUtilDenoiserInvokeTiled(OptixDenoiser denoiser, return OPTIX_SUCCESS; } +# if OPTIX_ABI_VERSION >= 55 +static void execute_optix_task(TaskPool &pool, OptixTask task, OptixResult &failure_reason) +{ + OptixTask additional_tasks[16]; + unsigned int num_additional_tasks = 0; + + const OptixResult result = optixTaskExecute(task, additional_tasks, 16, &num_additional_tasks); + if (result == OPTIX_SUCCESS) { + for (unsigned int i = 0; i < num_additional_tasks; ++i) { + pool.push(function_bind( + &execute_optix_task, std::ref(pool), additional_tasks[i], std::ref(failure_reason))); + } + } + else { + failure_reason = result; + } +} +# endif + } // namespace OptiXDevice::Denoiser::Denoiser(OptiXDevice *device) @@ -453,6 +473,23 @@ bool OptiXDevice::load_kernels(const uint kernel_features) return false; } +# if OPTIX_ABI_VERSION >= 55 + OptixTask task = nullptr; + OptixResult result = optixModuleCreateFromPTXWithTasks(context, + &module_options, + &pipeline_options, + ptx_data.data(), + ptx_data.size(), + nullptr, + nullptr, + &optix_module, + &task); + if (result == OPTIX_SUCCESS) { + TaskPool pool; + execute_optix_task(pool, task, result); + pool.wait_work(); + } +# else const OptixResult result = optixModuleCreateFromPTX(context, &module_options, &pipeline_options, @@ -461,6 +498,7 @@ bool OptiXDevice::load_kernels(const uint kernel_features) nullptr, 0, &optix_module); +# endif if (result != OPTIX_SUCCESS) { set_error(string_printf("Failed to load OptiX kernel from '%s' (%s)", ptx_filename.c_str(), diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index d97854a52d0..473bdb67920 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -145,6 +145,7 @@ set(SRC_KERNEL_SVM_HEADERS svm/normal.h svm/ramp.h svm/ramp_util.h + svm/sepcomb_color.h svm/sepcomb_hsv.h svm/sepcomb_vector.h svm/sky.h diff --git a/intern/cycles/kernel/device/metal/compat.h b/intern/cycles/kernel/device/metal/compat.h index 4e309f16c08..0ed52074a90 100644 --- a/intern/cycles/kernel/device/metal/compat.h +++ b/intern/cycles/kernel/device/metal/compat.h @@ -29,10 +29,26 @@ using namespace metal::raytracing; /* Qualifiers */ -#define ccl_device -#define ccl_device_inline ccl_device -#define ccl_device_forceinline ccl_device -#define ccl_device_noinline ccl_device __attribute__((noinline)) +#if defined(__KERNEL_METAL_APPLE__) + +/* Inline everything for Apple GPUs. + * This gives ~1.1x speedup and 10% spill reduction for integator_shade_surface + * at the cost of longer compile times (~4.5 minutes on M1 Max). */ + +# define ccl_device __attribute__((always_inline)) +# define ccl_device_inline __attribute__((always_inline)) +# define ccl_device_forceinline __attribute__((always_inline)) +# define ccl_device_noinline __attribute__((always_inline)) + +#else + +# define ccl_device +# define ccl_device_inline ccl_device +# define ccl_device_forceinline ccl_device +# define ccl_device_noinline ccl_device __attribute__((noinline)) + +#endif + #define ccl_device_noinline_cpu ccl_device #define ccl_device_inline_method ccl_device #define ccl_global device diff --git a/intern/cycles/kernel/osl/services.cpp b/intern/cycles/kernel/osl/services.cpp index 832498f1f73..e2e10b5b83f 100644 --- a/intern/cycles/kernel/osl/services.cpp +++ b/intern/cycles/kernel/osl/services.cpp @@ -1304,8 +1304,38 @@ bool OSLRenderServices::texture(ustring filename, break; } case OSLTextureHandle::SVM: { - /* Packed texture. */ - float4 rgba = kernel_tex_image_interp(kernel_globals, handle->svm_slot, s, 1.0f - t); + int id = -1; + if (handle->svm_slots[0].w == -1) { + /* Packed single texture. */ + id = handle->svm_slots[0].y; + } + else { + /* Packed tiled texture. */ + int tx = (int)s; + int ty = (int)t; + int tile = 1001 + 10 * ty + tx; + for (int4 tile_node : handle->svm_slots) { + if (tile_node.x == tile) { + id = tile_node.y; + break; + } + if (tile_node.z == tile) { + id = tile_node.w; + break; + } + } + s -= tx; + t -= ty; + } + + float4 rgba; + if (id == -1) { + rgba = make_float4( + TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A); + } + else { + rgba = kernel_tex_image_interp(kernel_globals, id, s, 1.0f - t); + } result[0] = rgba[0]; if (nchannels > 1) @@ -1319,7 +1349,7 @@ bool OSLRenderServices::texture(ustring filename, } case OSLTextureHandle::IES: { /* IES light. */ - result[0] = kernel_ies_interp(kernel_globals, handle->svm_slot, s, t); + result[0] = kernel_ies_interp(kernel_globals, handle->svm_slots[0].y, s, t); status = true; break; } @@ -1413,7 +1443,7 @@ bool OSLRenderServices::texture3d(ustring filename, /* Packed texture. */ ShaderData *sd = (ShaderData *)(sg->renderstate); KernelGlobals kernel_globals = sd->osl_globals; - int slot = handle->svm_slot; + int slot = handle->svm_slots[0].y; float3 P_float3 = make_float3(P.x, P.y, P.z); float4 rgba = kernel_tex_image_interp_3d(kernel_globals, slot, P_float3, INTERPOLATION_NONE); diff --git a/intern/cycles/kernel/osl/services.h b/intern/cycles/kernel/osl/services.h index 653fa017140..edffd912bad 100644 --- a/intern/cycles/kernel/osl/services.h +++ b/intern/cycles/kernel/osl/services.h @@ -39,18 +39,26 @@ struct KernelGlobalsCPU; * with additional data. * * These are stored in a concurrent hash map, because OSL can compile multiple - * shaders in parallel. */ + * shaders in parallel. + * + * NOTE: The svm_slots array contains a compressed mapping of tile to svm_slot pairs + * stored as follows: x:tile_a, y:svm_slot_a, z:tile_b, w:svm_slot_b etc. */ struct OSLTextureHandle : public OIIO::RefCnt { enum Type { OIIO, SVM, IES, BEVEL, AO }; + OSLTextureHandle(Type type, const vector<int4> &svm_slots) + : type(type), svm_slots(svm_slots), oiio_handle(NULL), processor(NULL) + { + } + OSLTextureHandle(Type type = OIIO, int svm_slot = -1) - : type(type), svm_slot(svm_slot), oiio_handle(NULL), processor(NULL) + : OSLTextureHandle(type, {make_int4(0, svm_slot, -1, -1)}) { } Type type; - int svm_slot; + vector<int4> svm_slots; OSL::TextureSystem::TextureHandle *oiio_handle; ColorSpaceProcessor *processor; }; diff --git a/intern/cycles/kernel/osl/shaders/CMakeLists.txt b/intern/cycles/kernel/osl/shaders/CMakeLists.txt index 7ced21c5670..741bce7c399 100644 --- a/intern/cycles/kernel/osl/shaders/CMakeLists.txt +++ b/intern/cycles/kernel/osl/shaders/CMakeLists.txt @@ -16,6 +16,7 @@ set(SRC_OSL node_camera.osl node_checker_texture.osl node_clamp.osl + node_combine_color.osl node_combine_rgb.osl node_combine_hsv.osl node_combine_xyz.osl @@ -68,6 +69,7 @@ set(SRC_OSL node_refraction_bsdf.osl node_rgb_curves.osl node_rgb_ramp.osl + node_separate_color.osl node_separate_rgb.osl node_separate_hsv.osl node_separate_xyz.osl diff --git a/intern/cycles/kernel/osl/shaders/node_color.h b/intern/cycles/kernel/osl/shaders/node_color.h index 388dd114e9a..06735f5b03d 100644 --- a/intern/cycles/kernel/osl/shaders/node_color.h +++ b/intern/cycles/kernel/osl/shaders/node_color.h @@ -148,3 +148,53 @@ color hsv_to_rgb(color hsv) return rgb; } + +color rgb_to_hsl(color rgb) +{ + float cmax, cmin, h, s, l; + + cmax = max(rgb[0], max(rgb[1], rgb[2])); + cmin = min(rgb[0], min(rgb[1], rgb[2])); + l = min(1.0, (cmax + cmin) / 2.0); + + if (cmax == cmin) { + h = s = 0.0; /* achromatic */ + } + else { + float cdelta = cmax - cmin; + s = l > 0.5 ? cdelta / (2.0 - cmax - cmin) : cdelta / (cmax + cmin); + if (cmax == rgb[0]) { + h = (rgb[1] - rgb[2]) / cdelta + (rgb[1] < rgb[2] ? 6.0 : 0.0); + } + else if (cmax == rgb[1]) { + h = (rgb[2] - rgb[0]) / cdelta + 2.0; + } + else { + h = (rgb[0] - rgb[1]) / cdelta + 4.0; + } + } + h /= 6.0; + + return color(h, s, l); +} + +color hsl_to_rgb(color hsl) +{ + float nr, ng, nb, chroma, h, s, l; + + h = hsl[0]; + s = hsl[1]; + l = hsl[2]; + + nr = abs(h * 6.0 - 3.0) - 1.0; + ng = 2.0 - abs(h * 6.0 - 2.0); + nb = 2.0 - abs(h * 6.0 - 4.0); + + nr = clamp(nr, 0.0, 1.0); + nb = clamp(nb, 0.0, 1.0); + ng = clamp(ng, 0.0, 1.0); + + chroma = (1.0 - abs(2.0 * l - 1.0)) * s; + + return color((nr - 0.5) * chroma + l, (ng - 0.5) * chroma + l, (nb - 0.5) * chroma + l); +} diff --git a/intern/cycles/kernel/osl/shaders/node_combine_color.osl b/intern/cycles/kernel/osl/shaders/node_combine_color.osl new file mode 100644 index 00000000000..681a592d2bb --- /dev/null +++ b/intern/cycles/kernel/osl/shaders/node_combine_color.osl @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#include "stdcycles.h" + +shader node_combine_color(string color_type = "rgb", + float Red = 0.0, + float Green = 0.0, + float Blue = 0.0, + output color Color = 0.8) +{ + if (color_type == "rgb" || color_type == "hsv" || color_type == "hsl") + Color = color(color_type, Red, Green, Blue); + else + warning("%s", "Unknown color space!"); +} diff --git a/intern/cycles/kernel/osl/shaders/node_separate_color.osl b/intern/cycles/kernel/osl/shaders/node_separate_color.osl new file mode 100644 index 00000000000..6f3e3149d8e --- /dev/null +++ b/intern/cycles/kernel/osl/shaders/node_separate_color.osl @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#include "node_color.h" +#include "stdcycles.h" + +shader node_separate_color(string color_type = "rgb", + color Color = 0.8, + output float Red = 0.0, + output float Green = 0.0, + output float Blue = 0.0) +{ + color col; + if (color_type == "rgb") + col = Color; + else if (color_type == "hsv") + col = rgb_to_hsv(Color); + else if (color_type == "hsl") + col = rgb_to_hsl(Color); + else + warning("%s", "Unknown color space!"); + + Red = col[0]; + Green = col[1]; + Blue = col[2]; +} diff --git a/intern/cycles/kernel/svm/color_util.h b/intern/cycles/kernel/svm/color_util.h index b439721383c..fa22d4bc8c2 100644 --- a/intern/cycles/kernel/svm/color_util.h +++ b/intern/cycles/kernel/svm/color_util.h @@ -307,4 +307,30 @@ ccl_device_inline float3 svm_brightness_contrast(float3 color, float brightness, return color; } +ccl_device float3 svm_combine_color(NodeCombSepColorType type, float3 color) +{ + switch (type) { + case NODE_COMBSEP_COLOR_HSV: + return hsv_to_rgb(color); + case NODE_COMBSEP_COLOR_HSL: + return hsl_to_rgb(color); + case NODE_COMBSEP_COLOR_RGB: + default: + return color; + } +} + +ccl_device float3 svm_separate_color(NodeCombSepColorType type, float3 color) +{ + switch (type) { + case NODE_COMBSEP_COLOR_HSV: + return rgb_to_hsv(color); + case NODE_COMBSEP_COLOR_HSL: + return rgb_to_hsl(color); + case NODE_COMBSEP_COLOR_RGB: + default: + return color; + } +} + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/sepcomb_color.h b/intern/cycles/kernel/svm/sepcomb_color.h new file mode 100644 index 00000000000..d186e7f163b --- /dev/null +++ b/intern/cycles/kernel/svm/sepcomb_color.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +ccl_device_noinline void svm_node_combine_color(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint color_type, + uint inputs_stack_offsets, + uint result_stack_offset) +{ + uint red_stack_offset, green_stack_offset, blue_stack_offset; + svm_unpack_node_uchar3( + inputs_stack_offsets, &red_stack_offset, &green_stack_offset, &blue_stack_offset); + + float r = stack_load_float(stack, red_stack_offset); + float g = stack_load_float(stack, green_stack_offset); + float b = stack_load_float(stack, blue_stack_offset); + + /* Combine, and convert back to RGB */ + float3 color = svm_combine_color((NodeCombSepColorType)color_type, make_float3(r, g, b)); + + if (stack_valid(result_stack_offset)) + stack_store_float3(stack, result_stack_offset, color); +} + +ccl_device_noinline void svm_node_separate_color(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint color_type, + uint input_stack_offset, + uint results_stack_offsets) +{ + float3 color = stack_load_float3(stack, input_stack_offset); + + /* Convert color space */ + color = svm_separate_color((NodeCombSepColorType)color_type, color); + + uint red_stack_offset, green_stack_offset, blue_stack_offset; + svm_unpack_node_uchar3( + results_stack_offsets, &red_stack_offset, &green_stack_offset, &blue_stack_offset); + + if (stack_valid(red_stack_offset)) + stack_store_float(stack, red_stack_offset, color.x); + if (stack_valid(green_stack_offset)) + stack_store_float(stack, green_stack_offset, color.y); + if (stack_valid(blue_stack_offset)) + stack_store_float(stack, blue_stack_offset, color.z); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 08352a6231f..5def943c87f 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -181,6 +181,7 @@ CCL_NAMESPACE_END #include "kernel/svm/noisetex.h" #include "kernel/svm/normal.h" #include "kernel/svm/ramp.h" +#include "kernel/svm/sepcomb_color.h" #include "kernel/svm/sepcomb_hsv.h" #include "kernel/svm/sepcomb_vector.h" #include "kernel/svm/sky.h" @@ -508,6 +509,12 @@ ccl_device void svm_eval_nodes(KernelGlobals kg, case NODE_MIX: offset = svm_node_mix(kg, sd, stack, node.y, node.z, node.w, offset); break; + case NODE_SEPARATE_COLOR: + svm_node_separate_color(kg, sd, stack, node.y, node.z, node.w); + break; + case NODE_COMBINE_COLOR: + svm_node_combine_color(kg, sd, stack, node.y, node.z, node.w); + break; case NODE_SEPARATE_VECTOR: svm_node_separate_vector(sd, stack, node.y, node.z, node.w); break; diff --git a/intern/cycles/kernel/svm/types.h b/intern/cycles/kernel/svm/types.h index bede58f7a54..82109ec4c4f 100644 --- a/intern/cycles/kernel/svm/types.h +++ b/intern/cycles/kernel/svm/types.h @@ -92,6 +92,8 @@ typedef enum ShaderNodeType { NODE_NORMAL_MAP, NODE_INVERT, NODE_MIX, + NODE_SEPARATE_COLOR, + NODE_COMBINE_COLOR, NODE_SEPARATE_VECTOR, NODE_COMBINE_VECTOR, NODE_SEPARATE_HSV, @@ -487,6 +489,12 @@ typedef enum NodePrincipledHairParametrization { NODE_PRINCIPLED_HAIR_NUM, } NodePrincipledHairParametrization; +typedef enum NodeCombSepColorType { + NODE_COMBSEP_COLOR_RGB, + NODE_COMBSEP_COLOR_HSV, + NODE_COMBSEP_COLOR_HSL, +} NodeCombSepColorType; + /* Closure */ typedef enum ClosureType { diff --git a/intern/cycles/scene/image.cpp b/intern/cycles/scene/image.cpp index 21fde88915e..c61ad1f1d71 100644 --- a/intern/cycles/scene/image.cpp +++ b/intern/cycles/scene/image.cpp @@ -117,12 +117,12 @@ void ImageHandle::clear() manager = NULL; } -bool ImageHandle::empty() +bool ImageHandle::empty() const { return tile_slots.empty(); } -int ImageHandle::num_tiles() +int ImageHandle::num_tiles() const { return tile_slots.size(); } @@ -154,6 +154,35 @@ int ImageHandle::svm_slot(const int tile_index) const return tile_slots[tile_index]; } +vector<int4> ImageHandle::get_svm_slots() const +{ + const size_t num_nodes = divide_up(tile_slots.size(), 2); + + vector<int4> svm_slots; + svm_slots.reserve(num_nodes); + for (size_t i = 0; i < num_nodes; i++) { + int4 node; + + int slot = tile_slots[2 * i]; + node.x = manager->images[slot]->loader->get_tile_number(); + node.y = slot; + + if ((2 * i + 1) < tile_slots.size()) { + slot = tile_slots[2 * i + 1]; + node.z = manager->images[slot]->loader->get_tile_number(); + node.w = slot; + } + else { + node.z = -1; + node.w = -1; + } + + svm_slots.push_back(node); + } + + return svm_slots; +} + device_texture *ImageHandle::image_memory(const int tile_index) const { if (tile_index >= tile_slots.size()) { @@ -266,6 +295,11 @@ ustring ImageLoader::osl_filepath() const return ustring(); } +int ImageLoader::get_tile_number() const +{ + return 0; +} + bool ImageLoader::equals(const ImageLoader *a, const ImageLoader *b) { if (a == NULL && b == NULL) { @@ -397,6 +431,19 @@ ImageHandle ImageManager::add_image(ImageLoader *loader, return handle; } +ImageHandle ImageManager::add_image(const vector<ImageLoader *> &loaders, + const ImageParams ¶ms) +{ + ImageHandle handle; + for (ImageLoader *loader : loaders) { + const int slot = add_image_slot(loader, params, true); + handle.tile_slots.push_back(slot); + } + + handle.manager = this; + return handle; +} + int ImageManager::add_image_slot(ImageLoader *loader, const ImageParams ¶ms, const bool builtin) diff --git a/intern/cycles/scene/image.h b/intern/cycles/scene/image.h index 4d0dee35eca..9edb6a7eaf5 100644 --- a/intern/cycles/scene/image.h +++ b/intern/cycles/scene/image.h @@ -112,6 +112,9 @@ class ImageLoader { /* Optional for OSL texture cache. */ virtual ustring osl_filepath() const; + /* Optional for tiled textures loaded externally. */ + virtual int get_tile_number() const; + /* Free any memory used for loading metadata and pixels. */ virtual void cleanup(){}; @@ -139,11 +142,12 @@ class ImageHandle { void clear(); - bool empty(); - int num_tiles(); + bool empty() const; + int num_tiles() const; ImageMetaData metadata(); int svm_slot(const int tile_index = 0) const; + vector<int4> get_svm_slots() const; device_texture *image_memory(const int tile_index = 0) const; VDBImageLoader *vdb_loader(const int tile_index = 0) const; @@ -169,6 +173,7 @@ class ImageManager { const ImageParams ¶ms, const array<int> &tiles); ImageHandle add_image(ImageLoader *loader, const ImageParams ¶ms, const bool builtin = true); + ImageHandle add_image(const vector<ImageLoader *> &loaders, const ImageParams ¶ms); void device_update(Device *device, Scene *scene, Progress &progress); void device_update_slot(Device *device, Scene *scene, int slot, Progress *progress); diff --git a/intern/cycles/scene/osl.cpp b/intern/cycles/scene/osl.cpp index ffa1a2f5623..6698e6e2cce 100644 --- a/intern/cycles/scene/osl.cpp +++ b/intern/cycles/scene/osl.cpp @@ -1211,14 +1211,15 @@ void OSLCompiler::parameter_texture(const char *name, ustring filename, ustring parameter(name, filename); } -void OSLCompiler::parameter_texture(const char *name, int svm_slot) +void OSLCompiler::parameter_texture(const char *name, const ImageHandle &handle) { /* Texture loaded through SVM image texture system. We generate a unique * name, which ends up being used in OSLRenderServices::get_texture_handle * to get handle again. Note that this name must be unique between multiple * render sessions as the render services are shared. */ ustring filename(string_printf("@svm%d", texture_shared_unique_id++).c_str()); - services->textures.insert(filename, new OSLTextureHandle(OSLTextureHandle::SVM, svm_slot)); + services->textures.insert(filename, + new OSLTextureHandle(OSLTextureHandle::SVM, handle.get_svm_slots())); parameter(name, filename); } @@ -1290,7 +1291,7 @@ void OSLCompiler::parameter_texture(const char * /* name */, { } -void OSLCompiler::parameter_texture(const char * /* name */, int /* svm_slot */) +void OSLCompiler::parameter_texture(const char * /* name */, const ImageHandle & /*handle*/) { } diff --git a/intern/cycles/scene/osl.h b/intern/cycles/scene/osl.h index f0f97dbcaad..bf27069b1b1 100644 --- a/intern/cycles/scene/osl.h +++ b/intern/cycles/scene/osl.h @@ -147,7 +147,7 @@ class OSLCompiler { void parameter_attribute(const char *name, ustring s); void parameter_texture(const char *name, ustring filename, ustring colorspace); - void parameter_texture(const char *name, int svm_slot); + void parameter_texture(const char *name, const ImageHandle &handle); void parameter_texture_ies(const char *name, int svm_slot); ShaderType output_type() diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index 95fccf725f3..03c152928d5 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -19,7 +19,6 @@ #include "util/color.h" #include "util/foreach.h" #include "util/log.h" -#include "util/string.h" #include "util/transform.h" #include "kernel/tables.h" @@ -450,22 +449,19 @@ void ImageTextureNode::compile(OSLCompiler &compiler) const ustring known_colorspace = metadata.colorspace; if (handle.svm_slot() == -1) { - /* OIIO currently does not support <UVTILE> substitutions natively. Replace with a format they - * understand. */ - std::string osl_filename = filename.string(); - string_replace(osl_filename, "<UVTILE>", "<U>_<V>"); compiler.parameter_texture( - "filename", ustring(osl_filename), compress_as_srgb ? u_colorspace_raw : known_colorspace); + "filename", filename, compress_as_srgb ? u_colorspace_raw : known_colorspace); } else { - compiler.parameter_texture("filename", handle.svm_slot()); + compiler.parameter_texture("filename", handle); } const bool unassociate_alpha = !(ColorSpaceManager::colorspace_is_data(colorspace) || alpha_type == IMAGE_ALPHA_CHANNEL_PACKED || alpha_type == IMAGE_ALPHA_IGNORE); const bool is_tiled = (filename.find("<UDIM>") != string::npos || - filename.find("<UVTILE>") != string::npos); + filename.find("<UVTILE>") != string::npos) || + handle.num_tiles() > 1; compiler.parameter(this, "projection"); compiler.parameter(this, "projection_blend"); @@ -610,7 +606,7 @@ void EnvironmentTextureNode::compile(OSLCompiler &compiler) "filename", filename, compress_as_srgb ? u_colorspace_raw : known_colorspace); } else { - compiler.parameter_texture("filename", handle.svm_slot()); + compiler.parameter_texture("filename", handle); } compiler.parameter(this, "projection"); @@ -965,7 +961,7 @@ void SkyTextureNode::compile(OSLCompiler &compiler) compiler.parameter_array("nishita_data", sunsky.nishita_data, 10); /* nishita texture */ if (sky_type == NODE_SKY_NISHITA) { - compiler.parameter_texture("filename", handle.svm_slot()); + compiler.parameter_texture("filename", handle); } compiler.add(this, "node_sky_texture"); } @@ -1860,7 +1856,7 @@ void PointDensityTextureNode::compile(OSLCompiler &compiler) handle = image_manager->add_image(filename.string(), image_params()); } - compiler.parameter_texture("filename", handle.svm_slot()); + compiler.parameter_texture("filename", handle); if (space == NODE_TEX_VOXEL_SPACE_WORLD) { compiler.parameter("mapping", tfm); compiler.parameter("use_mapping", 1); @@ -5010,6 +5006,63 @@ void MixNode::constant_fold(const ConstantFolder &folder) } } +/* Combine Color */ + +NODE_DEFINE(CombineColorNode) +{ + NodeType *type = NodeType::add("combine_color", create, NodeType::SHADER); + + static NodeEnum type_enum; + type_enum.insert("rgb", NODE_COMBSEP_COLOR_RGB); + type_enum.insert("hsv", NODE_COMBSEP_COLOR_HSV); + type_enum.insert("hsl", NODE_COMBSEP_COLOR_HSL); + SOCKET_ENUM(color_type, "Type", type_enum, NODE_COMBSEP_COLOR_RGB); + + SOCKET_IN_FLOAT(r, "Red", 0.0f); + SOCKET_IN_FLOAT(g, "Green", 0.0f); + SOCKET_IN_FLOAT(b, "Blue", 0.0f); + + SOCKET_OUT_COLOR(color, "Color"); + + return type; +} + +CombineColorNode::CombineColorNode() : ShaderNode(get_node_type()) +{ +} + +void CombineColorNode::constant_fold(const ConstantFolder &folder) +{ + if (folder.all_inputs_constant()) { + folder.make_constant(svm_combine_color(color_type, make_float3(r, g, b))); + } +} + +void CombineColorNode::compile(SVMCompiler &compiler) +{ + ShaderInput *red_in = input("Red"); + ShaderInput *green_in = input("Green"); + ShaderInput *blue_in = input("Blue"); + ShaderOutput *color_out = output("Color"); + + int red_stack_offset = compiler.stack_assign(red_in); + int green_stack_offset = compiler.stack_assign(green_in); + int blue_stack_offset = compiler.stack_assign(blue_in); + int color_stack_offset = compiler.stack_assign(color_out); + + compiler.add_node( + NODE_COMBINE_COLOR, + color_type, + compiler.encode_uchar4(red_stack_offset, green_stack_offset, blue_stack_offset), + color_stack_offset); +} + +void CombineColorNode::compile(OSLCompiler &compiler) +{ + compiler.parameter(this, "color_type"); + compiler.add(this, "node_combine_color"); +} + /* Combine RGB */ NODE_DEFINE(CombineRGBNode) @@ -5250,6 +5303,70 @@ void BrightContrastNode::compile(OSLCompiler &compiler) compiler.add(this, "node_brightness"); } +/* Separate Color */ + +NODE_DEFINE(SeparateColorNode) +{ + NodeType *type = NodeType::add("separate_color", create, NodeType::SHADER); + + static NodeEnum type_enum; + type_enum.insert("rgb", NODE_COMBSEP_COLOR_RGB); + type_enum.insert("hsv", NODE_COMBSEP_COLOR_HSV); + type_enum.insert("hsl", NODE_COMBSEP_COLOR_HSL); + SOCKET_ENUM(color_type, "Type", type_enum, NODE_COMBSEP_COLOR_RGB); + + SOCKET_IN_COLOR(color, "Color", zero_float3()); + + SOCKET_OUT_FLOAT(r, "Red"); + SOCKET_OUT_FLOAT(g, "Green"); + SOCKET_OUT_FLOAT(b, "Blue"); + + return type; +} + +SeparateColorNode::SeparateColorNode() : ShaderNode(get_node_type()) +{ +} + +void SeparateColorNode::constant_fold(const ConstantFolder &folder) +{ + if (folder.all_inputs_constant()) { + float3 col = svm_separate_color(color_type, color); + + for (int channel = 0; channel < 3; channel++) { + if (outputs[channel] == folder.output) { + folder.make_constant(col[channel]); + return; + } + } + } +} + +void SeparateColorNode::compile(SVMCompiler &compiler) +{ + ShaderInput *color_in = input("Color"); + ShaderOutput *red_out = output("Red"); + ShaderOutput *green_out = output("Green"); + ShaderOutput *blue_out = output("Blue"); + + int color_stack_offset = compiler.stack_assign(color_in); + int red_stack_offset = compiler.stack_assign(red_out); + int green_stack_offset = compiler.stack_assign(green_out); + int blue_stack_offset = compiler.stack_assign(blue_out); + + compiler.add_node( + NODE_SEPARATE_COLOR, + color_type, + color_stack_offset, + compiler.encode_uchar4(red_stack_offset, green_stack_offset, blue_stack_offset)); +} + +void SeparateColorNode::compile(OSLCompiler &compiler) +{ + compiler.parameter(this, "color_type"); + compiler.add(this, "node_separate_color"); +} + /* Separate RGB */ NODE_DEFINE(SeparateRGBNode) diff --git a/intern/cycles/scene/shader_nodes.h b/intern/cycles/scene/shader_nodes.h index 9aef5d3151f..ac40a397c1e 100644 --- a/intern/cycles/scene/shader_nodes.h +++ b/intern/cycles/scene/shader_nodes.h @@ -1101,6 +1101,17 @@ class MixNode : public ShaderNode { NODE_SOCKET_API(float, fac) }; +class CombineColorNode : public ShaderNode { + public: + SHADER_NODE_CLASS(CombineColorNode) + void constant_fold(const ConstantFolder &folder); + + NODE_SOCKET_API(NodeCombSepColorType, color_type) + NODE_SOCKET_API(float, r) + NODE_SOCKET_API(float, g) + NODE_SOCKET_API(float, b) +}; + class CombineRGBNode : public ShaderNode { public: SHADER_NODE_CLASS(CombineRGBNode) @@ -1150,6 +1161,15 @@ class BrightContrastNode : public ShaderNode { NODE_SOCKET_API(float, contrast) }; +class SeparateColorNode : public ShaderNode { + public: + SHADER_NODE_CLASS(SeparateColorNode) + void constant_fold(const ConstantFolder &folder); + + NODE_SOCKET_API(NodeCombSepColorType, color_type) + NODE_SOCKET_API(float3, color) +}; + class SeparateRGBNode : public ShaderNode { public: SHADER_NODE_CLASS(SeparateRGBNode) diff --git a/intern/cycles/util/color.h b/intern/cycles/util/color.h index cccccde3ba6..795c3754976 100644 --- a/intern/cycles/util/color.h +++ b/intern/cycles/util/color.h @@ -152,6 +152,56 @@ ccl_device float3 hsv_to_rgb(float3 hsv) return rgb; } +ccl_device float3 rgb_to_hsl(float3 rgb) +{ + float cmax, cmin, h, s, l; + + cmax = fmaxf(rgb.x, fmaxf(rgb.y, rgb.z)); + cmin = min(rgb.x, min(rgb.y, rgb.z)); + l = min(1.0f, (cmax + cmin) / 2.0f); + + if (cmax == cmin) { + h = s = 0.0f; /* achromatic */ + } + else { + float cdelta = cmax - cmin; + s = l > 0.5f ? cdelta / (2.0f - cmax - cmin) : cdelta / (cmax + cmin); + if (cmax == rgb.x) { + h = (rgb.y - rgb.z) / cdelta + (rgb.y < rgb.z ? 6.0f : 0.0f); + } + else if (cmax == rgb.y) { + h = (rgb.z - rgb.x) / cdelta + 2.0f; + } + else { + h = (rgb.x - rgb.y) / cdelta + 4.0f; + } + } + h /= 6.0f; + + return make_float3(h, s, l); +} + +ccl_device float3 hsl_to_rgb(float3 hsl) +{ + float nr, ng, nb, chroma, h, s, l; + + h = hsl.x; + s = hsl.y; + l = hsl.z; + + nr = fabsf(h * 6.0f - 3.0f) - 1.0f; + ng = 2.0f - fabsf(h * 6.0f - 2.0f); + nb = 2.0f - fabsf(h * 6.0f - 4.0f); + + nr = clamp(nr, 0.0f, 1.0f); + nb = clamp(nb, 0.0f, 1.0f); + ng = clamp(ng, 0.0f, 1.0f); + + chroma = (1.0f - fabsf(2.0f * l - 1.0f)) * s; + + return make_float3((nr - 0.5f) * chroma + l, (ng - 0.5f) * chroma + l, (nb - 0.5f) * chroma + l); +} + ccl_device float3 xyY_to_xyz(float x, float y, float Y) { float X, Z; diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index 9421edecf12..dceb9ced803 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -376,6 +376,7 @@ elseif(WIN32) intern/GHOST_DisplayManagerWin32.cpp intern/GHOST_DropTargetWin32.cpp intern/GHOST_SystemWin32.cpp + intern/GHOST_TrackpadWin32.cpp intern/GHOST_WindowWin32.cpp intern/GHOST_Wintab.cpp @@ -384,6 +385,7 @@ elseif(WIN32) intern/GHOST_DropTargetWin32.h intern/GHOST_SystemWin32.h intern/GHOST_TaskbarWin32.h + intern/GHOST_TrackpadWin32.h intern/GHOST_WindowWin32.h intern/GHOST_Wintab.h ) diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index b6836614962..8677c0b9552 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -967,7 +967,7 @@ bool GHOST_SystemCocoa::processEvents(bool waitForEvent) return anyProcessed; } -// Note: called from NSApplication delegate +/* NOTE: called from #NSApplication delegate. */ GHOST_TSuccess GHOST_SystemCocoa::handleApplicationBecomeActiveEvent() { for (GHOST_IWindow *iwindow : m_windowManager->getWindows()) { @@ -1046,7 +1046,7 @@ void GHOST_SystemCocoa::notifyExternalEventProcessed() m_outsideLoopEventProcessed = true; } -// Note: called from NSWindow delegate +/* NOTE: called from #NSWindow delegate. */ GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa *window) { @@ -1108,7 +1108,7 @@ GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, return GHOST_kSuccess; } -// Note: called from NSWindow subclass +/* NOTE: called from #NSWindow subclass. */ GHOST_TSuccess GHOST_SystemCocoa::handleDraggingEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType, GHOST_WindowCocoa *window, diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 6f694bfd9a6..dae3d578fa0 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -761,7 +761,7 @@ static void data_device_selection(void *data, input->data_offer_copy_paste = data_offer; std::string mime_receive; - for (const std::string &type : {mime_text_utf8, mime_text_plain}) { + for (const std::string type : {mime_text_utf8, mime_text_plain}) { if (data_offer->types.count(type)) { mime_receive = type; break; diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 83869188b65..8e07bf4ea3d 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -8,12 +8,14 @@ #include "GHOST_SystemWin32.h" #include "GHOST_ContextD3D.h" #include "GHOST_EventDragnDrop.h" +#include "GHOST_EventTrackpad.h" #ifndef _WIN32_IE # define _WIN32_IE 0x0501 /* shipped before XP, so doesn't impose additional requirements */ #endif #include <commctrl.h> +#include <dwmapi.h> #include <psapi.h> #include <shellapi.h> #include <shellscalingapi.h> @@ -414,6 +416,8 @@ bool GHOST_SystemWin32::processEvents(bool waitForEvent) hasEventHandled = true; } + driveTrackpad(); + // Process all the events waiting for us while (::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) { // TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data. @@ -423,6 +427,8 @@ bool GHOST_SystemWin32::processEvents(bool waitForEvent) hasEventHandled = true; } + processTrackpad(); + /* PeekMessage above is allowed to dispatch messages to the wndproc without us * noticing, so we need to check the event manager here to see if there are * events waiting in the queue. @@ -1416,6 +1422,52 @@ bool GHOST_SystemWin32::processNDOF(RAWINPUT const &raw) } #endif // WITH_INPUT_NDOF +void GHOST_SystemWin32::driveTrackpad() +{ + GHOST_WindowWin32 *active_window = static_cast<GHOST_WindowWin32 *>( + getWindowManager()->getActiveWindow()); + if (active_window) { + active_window->updateDirectManipulation(); + } +} + +void GHOST_SystemWin32::processTrackpad() +{ + GHOST_WindowWin32 *active_window = static_cast<GHOST_WindowWin32 *>( + getWindowManager()->getActiveWindow()); + + if (!active_window) { + return; + } + + GHOST_TTrackpadInfo trackpad_info = active_window->getTrackpadInfo(); + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + + int32_t cursor_x, cursor_y; + system->getCursorPosition(cursor_x, cursor_y); + + if (trackpad_info.x != 0 || trackpad_info.y != 0) { + system->pushEvent(new GHOST_EventTrackpad(system->getMilliSeconds(), + active_window, + GHOST_kTrackpadEventScroll, + cursor_x, + cursor_y, + trackpad_info.x, + trackpad_info.y, + trackpad_info.isScrollDirectionInverted)); + } + if (trackpad_info.scale != 0) { + system->pushEvent(new GHOST_EventTrackpad(system->getMilliSeconds(), + active_window, + GHOST_kTrackpadEventMagnify, + cursor_x, + cursor_y, + trackpad_info.scale, + 0, + false)); + } +} + LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { GHOST_Event *event = NULL; @@ -1968,6 +2020,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, suggestedWindowRect->right - suggestedWindowRect->left, suggestedWindowRect->bottom - suggestedWindowRect->top, SWP_NOZORDER | SWP_NOACTIVATE); + + window->updateDPI(); } break; case WM_DISPLAYCHANGE: { @@ -1985,6 +2039,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, ::SetFocus(hwnd); } break; + case WM_SETTINGCHANGE: + /* Microsoft: "Note that some applications send this message with lParam set to NULL" */ + if ((lParam != NULL) && (wcscmp(LPCWSTR(lParam), L"ImmersiveColorSet") == 0)) { + window->ThemeRefresh(); + } + break; //////////////////////////////////////////////////////////////////////// // Window events, ignored //////////////////////////////////////////////////////////////////////// @@ -2056,6 +2116,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, * In GHOST, we let DefWindowProc call the timer callback. */ break; + case DM_POINTERHITTEST: + /* The DM_POINTERHITTEST message is sent to a window, when pointer input is first + * detected, in order to determine the most probable input target for Direct + * Manipulation. */ + window->onPointerHitTest(wParam); + break; } } else { diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h index 9f8d52f9ca3..689b78b0317 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.h +++ b/intern/ghost/intern/GHOST_SystemWin32.h @@ -407,6 +407,16 @@ class GHOST_SystemWin32 : public GHOST_System { #endif /** + * Drives Direct Manipulation update. + */ + void driveTrackpad(); + + /** + * Creates trackpad events for the active window. + */ + void processTrackpad(); + + /** * Returns the local state of the modifier keys (from the message queue). * \param keys: The state of the keys. */ diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index ebee5f58fff..bbce6fdfdb5 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -63,7 +63,7 @@ #include <stdio.h> /* for fprintf only */ #include <vector> -/* for debugging - so we can breakpoint X11 errors */ +/* For debugging, so we can break-point X11 errors. */ // #define USE_X11_ERROR_HANDLERS #ifdef WITH_X11_XINPUT @@ -2492,7 +2492,7 @@ GHOST_TSuccess GHOST_SystemX11::pushDragDropEvent(GHOST_TEventType eventType, } #endif /** - * These callbacks can be used for debugging, so we can breakpoint on an X11 error. + * These callbacks can be used for debugging, so we can break-point on an X11 error. * * Dummy function to get around IO Handler exiting if device invalid * Basically it will not crash blender now if you have a X device that diff --git a/intern/ghost/intern/GHOST_TrackpadWin32.cpp b/intern/ghost/intern/GHOST_TrackpadWin32.cpp new file mode 100644 index 00000000000..d5317f0f780 --- /dev/null +++ b/intern/ghost/intern/GHOST_TrackpadWin32.cpp @@ -0,0 +1,343 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup GHOST + */ + +#include <cmath> + +#include "GHOST_Debug.h" +#include "GHOST_TrackpadWin32.h" + +GHOST_DirectManipulationHelper::GHOST_DirectManipulationHelper( + HWND hWnd, + Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager, + Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager, + Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport, + Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler> + directManipulationEventHandler, + DWORD directManipulationViewportHandlerCookie, + bool isScrollDirectionInverted) + : m_hWnd(hWnd), + m_scrollDirectionRegKey(NULL), + m_scrollDirectionChangeEvent(NULL), + m_directManipulationManager(directManipulationManager), + m_directManipulationUpdateManager(directManipulationUpdateManager), + m_directManipulationViewport(directManipulationViewport), + m_directManipulationEventHandler(directManipulationEventHandler), + m_directManipulationViewportHandlerCookie(directManipulationViewportHandlerCookie), + m_isScrollDirectionInverted(isScrollDirectionInverted) +{ +} + +GHOST_DirectManipulationHelper *GHOST_DirectManipulationHelper::create(HWND hWnd, uint16_t dpi) +{ +#define DM_CHECK_RESULT_AND_EXIT_EARLY(hr, failMessage) \ + { \ + if (!SUCCEEDED(hr)) { \ + GHOST_PRINT(failMessage); \ + return nullptr; \ + } \ + } + + Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager; + HRESULT hr = ::CoCreateInstance(CLSID_DirectManipulationManager, + nullptr, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&directManipulationManager)); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "DirectManipulationManager create failed\n"); + + /* Since we want to use fake viewport, we need to send fake updates to UpdateManager. */ + Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager; + hr = directManipulationManager->GetUpdateManager(IID_PPV_ARGS(&directManipulationUpdateManager)); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Get UpdateManager failed\n"); + + Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport; + hr = directManipulationManager->CreateViewport( + nullptr, hWnd, IID_PPV_ARGS(&directManipulationViewport)); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport create failed\n"); + + DIRECTMANIPULATION_CONFIGURATION configuration = + DIRECTMANIPULATION_CONFIGURATION_INTERACTION | + DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X | + DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y | + DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA | + DIRECTMANIPULATION_CONFIGURATION_SCALING; + + hr = directManipulationViewport->ActivateConfiguration(configuration); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set ActivateConfiguration failed\n"); + + /* Since we are using fake viewport and only want to use Direct Manipulation for touchpad, we + * need to use MANUALUPDATE option. */ + hr = directManipulationViewport->SetViewportOptions( + DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set ViewportOptions failed\n"); + + /* We receive Direct Manipulation transform updates in IDirectManipulationViewportEventHandler + * callbacks. */ + Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler> + directManipulationEventHandler = + Microsoft::WRL::Make<GHOST_DirectManipulationViewportEventHandler>(dpi); + DWORD directManipulationViewportHandlerCookie; + directManipulationViewport->AddEventHandler( + hWnd, directManipulationEventHandler.Get(), &directManipulationViewportHandlerCookie); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport add EventHandler failed\n"); + + /* Set default rect for viewport before activating. */ + RECT rect = {0, 0, 10000, 10000}; + hr = directManipulationViewport->SetViewportRect(&rect); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set rect failed\n"); + + hr = directManipulationManager->Activate(hWnd); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "DirectManipulationManager activate failed\n"); + + hr = directManipulationViewport->Enable(); + DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport enable failed\n"); + + directManipulationEventHandler->resetViewport(directManipulationViewport.Get()); + + bool isScrollDirectionInverted = getScrollDirectionFromReg(); + + auto instance = new GHOST_DirectManipulationHelper(hWnd, + directManipulationManager, + directManipulationUpdateManager, + directManipulationViewport, + directManipulationEventHandler, + directManipulationViewportHandlerCookie, + isScrollDirectionInverted); + + instance->registerScrollDirectionChangeListener(); + + return instance; + +#undef DM_CHECK_RESULT_AND_EXIT_EARLY +} + +bool GHOST_DirectManipulationHelper::getScrollDirectionFromReg() +{ + DWORD scrollDirectionRegValue, pcbData; + HRESULT hr = HRESULT_FROM_WIN32( + RegGetValueW(HKEY_CURRENT_USER, + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PrecisionTouchPad\\", + L"ScrollDirection", + RRF_RT_REG_DWORD, + NULL, + &scrollDirectionRegValue, + &pcbData)); + if (!SUCCEEDED(hr)) { + GHOST_PRINT("Failed to get scroll direction from registry\n"); + return false; + } + + return scrollDirectionRegValue == 0; +} + +void GHOST_DirectManipulationHelper::registerScrollDirectionChangeListener() +{ + + if (!m_scrollDirectionRegKey) { + HRESULT hr = HRESULT_FROM_WIN32( + RegOpenKeyExW(HKEY_CURRENT_USER, + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PrecisionTouchPad\\", + 0, + KEY_NOTIFY, + &m_scrollDirectionRegKey)); + if (!SUCCEEDED(hr)) { + GHOST_PRINT("Failed to open scroll direction registry key\n"); + return; + } + } + + if (!m_scrollDirectionChangeEvent) { + m_scrollDirectionChangeEvent = CreateEventW(NULL, true, false, NULL); + } + else { + ResetEvent(m_scrollDirectionChangeEvent); + } + HRESULT hr = HRESULT_FROM_WIN32(RegNotifyChangeKeyValue(m_scrollDirectionRegKey, + true, + REG_NOTIFY_CHANGE_LAST_SET, + m_scrollDirectionChangeEvent, + true)); + if (!SUCCEEDED(hr)) { + GHOST_PRINT("Failed to register scroll direction change listener\n"); + return; + } +} + +void GHOST_DirectManipulationHelper::onPointerHitTest(UINT32 pointerId) +{ + [[maybe_unused]] HRESULT hr = m_directManipulationViewport->SetContact(pointerId); + GHOST_ASSERT(SUCCEEDED(hr), "Viewport set contact failed\n"); + + if (WaitForSingleObject(m_scrollDirectionChangeEvent, 0) == WAIT_OBJECT_0) { + m_isScrollDirectionInverted = getScrollDirectionFromReg(); + registerScrollDirectionChangeListener(); + } +} + +void GHOST_DirectManipulationHelper::update() +{ + if (m_directManipulationEventHandler->dm_status == DIRECTMANIPULATION_RUNNING || + m_directManipulationEventHandler->dm_status == DIRECTMANIPULATION_INERTIA) { + [[maybe_unused]] HRESULT hr = m_directManipulationUpdateManager->Update(nullptr); + GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationUpdateManager update failed\n"); + } +} + +void GHOST_DirectManipulationHelper::setDPI(uint16_t dpi) +{ + m_directManipulationEventHandler->dpi = dpi; +} + +GHOST_TTrackpadInfo GHOST_DirectManipulationHelper::getTrackpadInfo() +{ + GHOST_TTrackpadInfo result = m_directManipulationEventHandler->accumulated_values; + result.isScrollDirectionInverted = m_isScrollDirectionInverted; + + m_directManipulationEventHandler->accumulated_values = {0, 0, 0}; + return result; +} + +GHOST_DirectManipulationHelper::~GHOST_DirectManipulationHelper() +{ + HRESULT hr; + hr = m_directManipulationViewport->Stop(); + GHOST_ASSERT(SUCCEEDED(hr), "Viewport stop failed\n"); + + hr = m_directManipulationViewport->RemoveEventHandler(m_directManipulationViewportHandlerCookie); + GHOST_ASSERT(SUCCEEDED(hr), "Viewport remove event handler failed\n"); + + hr = m_directManipulationViewport->Abandon(); + GHOST_ASSERT(SUCCEEDED(hr), "Viewport abandon failed\n"); + + hr = m_directManipulationManager->Deactivate(m_hWnd); + GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationManager deactivate failed\n"); + + if (m_scrollDirectionChangeEvent) { + CloseHandle(m_scrollDirectionChangeEvent); + m_scrollDirectionChangeEvent = NULL; + } + if (m_scrollDirectionRegKey) { + RegCloseKey(m_scrollDirectionRegKey); + m_scrollDirectionRegKey = NULL; + } +} + +GHOST_DirectManipulationViewportEventHandler::GHOST_DirectManipulationViewportEventHandler( + uint16_t dpi) + : accumulated_values({0, 0, 0}), dpi(dpi), dm_status(DIRECTMANIPULATION_BUILDING) +{ +} + +void GHOST_DirectManipulationViewportEventHandler::resetViewport( + IDirectManipulationViewport *viewport) +{ + if (gesture_state != GESTURE_NONE) { + [[maybe_unused]] HRESULT hr = viewport->ZoomToRect(0.0f, 0.0f, 10000.0f, 10000.0f, FALSE); + GHOST_ASSERT(SUCCEEDED(hr), "Viewport reset failed\n"); + } + + gesture_state = GESTURE_NONE; + + last_scale = PINCH_SCALE_FACTOR; + last_x = 0.0f; + last_y = 0.0f; +} + +HRESULT GHOST_DirectManipulationViewportEventHandler::OnViewportStatusChanged( + IDirectManipulationViewport *viewport, + DIRECTMANIPULATION_STATUS current, + DIRECTMANIPULATION_STATUS previous) +{ + dm_status = current; + + if (current == previous) { + return S_OK; + } + + if (previous == DIRECTMANIPULATION_ENABLED || current == DIRECTMANIPULATION_READY || + (previous == DIRECTMANIPULATION_INERTIA && current != DIRECTMANIPULATION_INERTIA)) { + resetViewport(viewport); + } + + return S_OK; +} + +HRESULT GHOST_DirectManipulationViewportEventHandler::OnViewportUpdated( + IDirectManipulationViewport *viewport) +{ + /* Nothing to do here. */ + return S_OK; +} + +HRESULT GHOST_DirectManipulationViewportEventHandler::OnContentUpdated( + IDirectManipulationViewport *viewport, IDirectManipulationContent *content) +{ + float transform[6]; + HRESULT hr = content->GetContentTransform(transform, ARRAYSIZE(transform)); + GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationContent get transform failed\n"); + + const float device_scale_factor = dpi / 96.0f; + + const float scale = transform[0] * PINCH_SCALE_FACTOR; + const float x = transform[4] / device_scale_factor; + const float y = transform[5] / device_scale_factor; + + const float EPS = 3e-5; + + /* Ignore repeating or incorrect input. */ + if ((fabs(scale - last_scale) <= EPS && fabs(x - last_x) <= EPS && fabs(y - last_y) <= EPS) || + scale == 0.0f) { + GHOST_PRINT("Ignoring touchpad input\n"); + return hr; + } + + /* Assume that every gesture is a pan in the beginning. + * If it's a pinch, the gesture will be changed below. */ + if (gesture_state == GESTURE_NONE) { + gesture_state = GESTURE_PAN; + } + + /* DM doesn't always immediately recognize pinch gestures, + * so allow transition from pan to pinch. */ + if (gesture_state == GESTURE_PAN) { + if (fabs(scale - PINCH_SCALE_FACTOR) > EPS) { + gesture_state = GESTURE_PINCH; + } + } + + /* This state machine is used here because: + * 1. Pinch and pan gestures must be differentiated and cannot be processed at the same time + * because XY transform values become nonsensical during pinch gesture. + * 2. GHOST requires delta values for events while DM provides transformation matrix of the + * current gesture. + * 3. GHOST events accept integer values while DM values are non-integer. + * Truncated fractional parts are accumulated and accounted for in following updates. + */ + switch (gesture_state) { + case GESTURE_PINCH: { + int32_t dscale = roundf(scale - last_scale); + + last_scale += dscale; + + accumulated_values.scale += dscale; + break; + } + case GESTURE_PAN: { + int32_t dx = roundf(x - last_x); + int32_t dy = roundf(y - last_y); + + last_x += dx; + last_y += dy; + + accumulated_values.x += dx; + accumulated_values.y += dy; + break; + } + case GESTURE_NONE: + break; + } + + return hr; +} diff --git a/intern/ghost/intern/GHOST_TrackpadWin32.h b/intern/ghost/intern/GHOST_TrackpadWin32.h new file mode 100644 index 00000000000..2e28f756965 --- /dev/null +++ b/intern/ghost/intern/GHOST_TrackpadWin32.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup GHOST + * Declaration of GHOST DirectManipulation classes. + */ + +#pragma once + +#ifndef WIN32 +# error WIN32 only! +#endif // WIN32 + +#include "GHOST_Types.h" + +#include <directmanipulation.h> +#include <wrl.h> + +#define PINCH_SCALE_FACTOR 125.0f + +typedef struct { + int32_t x, y, scale; + bool isScrollDirectionInverted; +} GHOST_TTrackpadInfo; + +class GHOST_DirectManipulationHelper; + +class GHOST_DirectManipulationViewportEventHandler + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>, + Microsoft::WRL::Implements< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>, + Microsoft::WRL::FtmBase, + IDirectManipulationViewportEventHandler>> { + public: + GHOST_DirectManipulationViewportEventHandler(uint16_t dpi); + + /* + * Resets viewport and tracked touchpad state. + */ + void resetViewport(IDirectManipulationViewport *viewport); + + /* DirectManipulation callbacks. */ + HRESULT STDMETHODCALLTYPE OnViewportStatusChanged(IDirectManipulationViewport *viewport, + DIRECTMANIPULATION_STATUS current, + DIRECTMANIPULATION_STATUS previous) override; + + HRESULT STDMETHODCALLTYPE OnViewportUpdated(IDirectManipulationViewport *viewport) override; + + HRESULT STDMETHODCALLTYPE OnContentUpdated(IDirectManipulationViewport *viewport, + IDirectManipulationContent *content) override; + + private: + enum { GESTURE_NONE, GESTURE_PAN, GESTURE_PINCH } gesture_state; + + int32_t last_x, last_y, last_scale; + GHOST_TTrackpadInfo accumulated_values; + uint16_t dpi; + DIRECTMANIPULATION_STATUS dm_status; + + friend class GHOST_DirectManipulationHelper; +}; + +class GHOST_DirectManipulationHelper { + public: + /* + * Creates a GHOST_DirectManipulationHelper for the provided window. + * \param hWnd: The window receiving DirectManipulation events. + * \param dpi: The current DPI. + * \return Pointer to the new GHOST_DirectManipulationHelper if created, nullptr if there was an + * error. + */ + static GHOST_DirectManipulationHelper *create(HWND hWnd, uint16_t dpi); + + ~GHOST_DirectManipulationHelper(); + + /* + * Drives the DirectManipulation context. + * DirectManipulation's intended use is to tie user input into DirectComposition's compositor + * scaling and translating. We are not using DirectComposition and therefore must drive + * DirectManipulation manually. + */ + void update(); + + /* + * Sets pointer in contact with the DirectManipulation context. + * \param pointerId: ID of the pointer in contact. + */ + void onPointerHitTest(UINT32 pointerId); + + /* + * Updates DPI information for touchpad scaling. + * \param dpi: The new DPI. + */ + void setDPI(uint16_t dpi); + + /* + * Retrieves trackpad input. + * \return The accumulated trackpad translation and scale since last call. + */ + GHOST_TTrackpadInfo getTrackpadInfo(); + + private: + GHOST_DirectManipulationHelper( + HWND hWnd, + Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager, + Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager, + Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport, + Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler> + directManipulationEventHandler, + DWORD directManipulationViewportHandlerCookie, + bool isScrollDirectionInverted); + + /* + * Retrieves the scroll direction from the registry. + * \return True if scroll direction is inverted. + */ + static bool getScrollDirectionFromReg(); + + /* + * Registers listener for registry scroll direction entry changes. + */ + void registerScrollDirectionChangeListener(); + + HWND m_hWnd; + + HKEY m_scrollDirectionRegKey; + HANDLE m_scrollDirectionChangeEvent; + + Microsoft::WRL::ComPtr<IDirectManipulationManager> m_directManipulationManager; + Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> m_directManipulationUpdateManager; + Microsoft::WRL::ComPtr<IDirectManipulationViewport> m_directManipulationViewport; + Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler> + m_directManipulationEventHandler; + DWORD m_directManipulationViewportHandlerCookie; + + bool m_isScrollDirectionInverted; +}; diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm index 4a1b3c2fe16..e7f5fdaa011 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.mm +++ b/intern/ghost/intern/GHOST_WindowCocoa.mm @@ -426,8 +426,8 @@ GHOST_WindowCocoa::~GHOST_WindowCocoa() [m_window close]; } - // Check for other blender opened windows and make the frontmost key - // Note: for some reason the closed window is still in the list + /* Check for other blender opened windows and make the front-most key + * NOTE: for some reason the closed window is still in the list. */ NSArray *windowsList = [NSApp orderedWindows]; for (int a = 0; a < [windowsList count]; a++) { if (m_window != (CocoaWindow *)[windowsList objectAtIndex:a]) { diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index 2ce224b666b..2e17454d24f 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -16,9 +16,7 @@ #include "GHOST_ContextWGL.h" -#ifdef WIN32_COMPOSITING -# include <Dwmapi.h> -#endif +#include <Dwmapi.h> #include <assert.h> #include <math.h> @@ -70,6 +68,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, m_normal_state(GHOST_kWindowStateNormal), m_user32(::LoadLibrary("user32.dll")), m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP), + m_directManipulationHelper(NULL), m_debug_context(is_debug) { DWORD style = parentwindow ? @@ -172,6 +171,8 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, break; } + ThemeRefresh(); + ::ShowWindow(m_hWnd, nCmdShow); #ifdef WIN32_COMPOSITING @@ -204,6 +205,42 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, /* Allow the showing of a progress bar on the taskbar. */ CoCreateInstance( CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar); + + /* Initialize Direct Manipulation. */ + m_directManipulationHelper = GHOST_DirectManipulationHelper::create(m_hWnd, getDPIHint()); +} + +void GHOST_WindowWin32::updateDirectManipulation() +{ + if (!m_directManipulationHelper) { + return; + } + + m_directManipulationHelper->update(); +} + +void GHOST_WindowWin32::onPointerHitTest(WPARAM wParam) +{ + /* Only DM_POINTERHITTEST can be the first message of input sequence of touchpad input. */ + + if (!m_directManipulationHelper) { + return; + } + + UINT32 pointerId = GET_POINTERID_WPARAM(wParam); + POINTER_INPUT_TYPE pointerType; + if (GetPointerType(pointerId, &pointerType) && pointerType == PT_TOUCHPAD) { + m_directManipulationHelper->onPointerHitTest(pointerId); + } +} + +GHOST_TTrackpadInfo GHOST_WindowWin32::getTrackpadInfo() +{ + if (!m_directManipulationHelper) { + return {0, 0, 0}; + } + + return m_directManipulationHelper->getTrackpadInfo(); } GHOST_WindowWin32::~GHOST_WindowWin32() @@ -253,6 +290,9 @@ GHOST_WindowWin32::~GHOST_WindowWin32() ::DestroyWindow(m_hWnd); m_hWnd = 0; } + + delete m_directManipulationHelper; + m_directManipulationHelper = NULL; } void GHOST_WindowWin32::adjustWindowRectForClosestMonitor(LPRECT win_rect, @@ -282,7 +322,7 @@ void GHOST_WindowWin32::adjustWindowRectForClosestMonitor(LPRECT win_rect, } /* Adjust to allow for caption, borders, shadows, scaling, etc. Resulting values can be - * correctly outside of monitor bounds. Note: You cannot specify WS_OVERLAPPED when calling. */ + * correctly outside of monitor bounds. NOTE: You cannot specify #WS_OVERLAPPED when calling. */ if (fpAdjustWindowRectExForDpi) { UINT dpiX, dpiY; GetDpiForMonitor(hmonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); @@ -1016,6 +1056,32 @@ GHOST_TabletData GHOST_WindowWin32::getTabletData() } } +void GHOST_WindowWin32::ThemeRefresh() +{ + DWORD lightMode; + DWORD pcbData = sizeof(lightMode); + if (RegGetValueW(HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize\\", + L"AppsUseLightTheme", + RRF_RT_REG_DWORD, + NULL, + &lightMode, + &pcbData) == ERROR_SUCCESS) { + BOOL DarkMode = !lightMode; + + /* 20 == DWMWA_USE_IMMERSIVE_DARK_MODE in Windows 11 SDK. This value was undocumented for + * Windows 10 versions 2004 and later, supported for Windows 11 Build 22000 and later. */ + DwmSetWindowAttribute(this->m_hWnd, 20, &DarkMode, sizeof(DarkMode)); + } +} + +void GHOST_WindowWin32::updateDPI() +{ + if (m_directManipulationHelper) { + m_directManipulationHelper->setDPI(getDPIHint()); + } +} + uint16_t GHOST_WindowWin32::getDPIHint() { if (m_user32) { diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index d5f47871aff..c958a89ac48 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -13,6 +13,7 @@ #endif // WIN32 #include "GHOST_TaskbarWin32.h" +#include "GHOST_TrackpadWin32.h" #include "GHOST_Window.h" #include "GHOST_Wintab.h" #ifdef WITH_INPUT_IME @@ -286,6 +287,8 @@ class GHOST_WindowWin32 : public GHOST_Window { return GHOST_kFailure; } + void updateDPI(); + uint16_t getDPIHint() override; /** True if the mouse is either over or captured by the window. */ @@ -294,6 +297,9 @@ class GHOST_WindowWin32 : public GHOST_Window { /** True if the window currently resizing. */ bool m_inLiveResize; + /** Called when OS colors change and when the window is created. */ + void ThemeRefresh(); + #ifdef WITH_INPUT_IME GHOST_ImeWin32 *getImeInput() { @@ -305,6 +311,19 @@ class GHOST_WindowWin32 : public GHOST_Window { void endIME(); #endif /* WITH_INPUT_IME */ + /* + * Drive DirectManipulation context. + */ + void updateDirectManipulation(); + + /* + * Handle DM_POINTERHITTEST events. + * \param wParam: wParam from the event. + */ + void onPointerHitTest(WPARAM wParam); + + GHOST_TTrackpadInfo getTrackpadInfo(); + private: /** * \param type: The type of rendering context create. @@ -388,6 +407,8 @@ class GHOST_WindowWin32 : public GHOST_Window { HWND m_parentWindowHwnd; + GHOST_DirectManipulationHelper *m_directManipulationHelper; + #ifdef WITH_INPUT_IME /** Handle input method editors event */ GHOST_ImeWin32 m_imeInput; diff --git a/intern/guardedalloc/intern/mallocn_guarded_impl.c b/intern/guardedalloc/intern/mallocn_guarded_impl.c index acad413b4c1..8bf1680e6f8 100644 --- a/intern/guardedalloc/intern/mallocn_guarded_impl.c +++ b/intern/guardedalloc/intern/mallocn_guarded_impl.c @@ -54,7 +54,7 @@ # define DEBUG_MEMCOUNTER_ERROR_VAL 0 static int _mallocn_count = 0; -/* breakpoint here */ +/* Break-point here. */ static void memcount_raise(const char *name) { fprintf(stderr, "%s: memcount-leak, %d\n", name, _mallocn_count); diff --git a/intern/iksolver/intern/IK_QTask.cpp b/intern/iksolver/intern/IK_QTask.cpp index caf9585a94e..80eda01b17f 100644 --- a/intern/iksolver/intern/IK_QTask.cpp +++ b/intern/iksolver/intern/IK_QTask.cpp @@ -116,7 +116,7 @@ void IK_QOrientationTask::ComputeJacobian(IK_QJacobian &jacobian) } // IK_QCenterOfMassTask -// Note: implementation not finished! +// NOTE: implementation not finished! IK_QCenterOfMassTask::IK_QCenterOfMassTask(bool primary, const IK_QSegment *segment, diff --git a/intern/libmv/CMakeLists.txt b/intern/libmv/CMakeLists.txt index f9fef9f7a29..e0ed68eb20e 100644 --- a/intern/libmv/CMakeLists.txt +++ b/intern/libmv/CMakeLists.txt @@ -26,7 +26,6 @@ if(WITH_LIBMV) endif() add_definitions(${GFLAGS_DEFINES}) add_definitions(${GLOG_DEFINES}) - add_definitions(${CERES_DEFINES}) add_definitions(-DLIBMV_GFLAGS_NAMESPACE=${GFLAGS_NAMESPACE}) list(APPEND INC diff --git a/intern/libmv/bundle.sh b/intern/libmv/bundle.sh index 6808e244c05..82293068745 100755 --- a/intern/libmv/bundle.sh +++ b/intern/libmv/bundle.sh @@ -124,7 +124,6 @@ if(WITH_LIBMV) endif() add_definitions(\${GFLAGS_DEFINES}) add_definitions(\${GLOG_DEFINES}) - add_definitions(\${CERES_DEFINES}) add_definitions(-DLIBMV_GFLAGS_NAMESPACE=\${GFLAGS_NAMESPACE}) list(APPEND INC diff --git a/intern/libmv/libmv/multiview/euclidean_resection.h b/intern/libmv/libmv/multiview/euclidean_resection.h index 3c4c3979ff6..cdb9b5af52e 100644 --- a/intern/libmv/libmv/multiview/euclidean_resection.h +++ b/intern/libmv/libmv/multiview/euclidean_resection.h @@ -118,7 +118,7 @@ void EuclideanResectionAnsarDaniilidis(const Mat2X& x_camera, * This is the algorithm described in: * "{EP$n$P: An Accurate $O(n)$ Solution to the P$n$P Problem", by V. Lepetit * and F. Moreno-Noguer and P. Fua, IJCV 2009. vol. 81, no. 2 - * \note: the non-linear optimization is not implemented here. + * \note the non-linear optimization is not implemented here. */ bool EuclideanResectionEPnP(const Mat2X& x_camera, const Mat3X& X_world, diff --git a/intern/libmv/libmv/simple_pipeline/bundle.cc b/intern/libmv/libmv/simple_pipeline/bundle.cc index e86c3bca57f..355c167d000 100644 --- a/intern/libmv/libmv/simple_pipeline/bundle.cc +++ b/intern/libmv/libmv/simple_pipeline/bundle.cc @@ -685,7 +685,7 @@ void EuclideanBundleCommonIntrinsics(const Tracks& tracks, PackCamerasRotationAndTranslation(*reconstruction); // Parameterization used to restrict camera motion for modal solvers. - ceres::SubsetParameterization* constant_translation_parameterization = NULL; + ceres::SubsetManifold* constant_translation_manifold = NULL; if (bundle_constraints & BUNDLE_NO_TRANSLATION) { std::vector<int> constant_translation; @@ -694,8 +694,8 @@ void EuclideanBundleCommonIntrinsics(const Tracks& tracks, constant_translation.push_back(4); constant_translation.push_back(5); - constant_translation_parameterization = - new ceres::SubsetParameterization(6, constant_translation); + constant_translation_manifold = + new ceres::SubsetManifold(6, constant_translation); } // Add residual blocks to the problem. @@ -735,8 +735,7 @@ void EuclideanBundleCommonIntrinsics(const Tracks& tracks, } if (bundle_constraints & BUNDLE_NO_TRANSLATION) { - problem.SetParameterization(current_camera_R_t, - constant_translation_parameterization); + problem.SetManifold(current_camera_R_t, constant_translation_manifold); } zero_weight_tracks_flags[marker.track] = false; @@ -787,11 +786,11 @@ void EuclideanBundleCommonIntrinsics(const Tracks& tracks, #undef MAYBE_SET_CONSTANT if (!constant_intrinsics.empty()) { - ceres::SubsetParameterization* subset_parameterization = - new ceres::SubsetParameterization(PackedIntrinsics::NUM_PARAMETERS, - constant_intrinsics); + ceres::SubsetManifold* subset_parameterization = + new ceres::SubsetManifold(PackedIntrinsics::NUM_PARAMETERS, + constant_intrinsics); - problem.SetParameterization(intrinsics_block, subset_parameterization); + problem.SetManifold(intrinsics_block, subset_parameterization); } } diff --git a/intern/libmv/libmv/simple_pipeline/modal_solver.cc b/intern/libmv/libmv/simple_pipeline/modal_solver.cc index 845b299e31e..206d264f1f8 100644 --- a/intern/libmv/libmv/simple_pipeline/modal_solver.cc +++ b/intern/libmv/libmv/simple_pipeline/modal_solver.cc @@ -180,7 +180,7 @@ void ModalSolver(const Tracks& tracks, // NOTE: Parameterization is lazily initialized when it is really needed, // and is re-used by all parameters block. - ceres::LocalParameterization* quaternion_parameterization = NULL; + ceres::Manifold* quaternion_manifold = NULL; int num_residuals = 0; for (int i = 0; i < all_markers.size(); ++i) { @@ -197,12 +197,11 @@ void ModalSolver(const Tracks& tracks, &quaternion(0)); num_residuals++; - if (quaternion_parameterization == NULL) { - quaternion_parameterization = new ceres::QuaternionParameterization(); + if (quaternion_manifold == NULL) { + quaternion_manifold = new ceres::QuaternionManifold(); } - problem.SetParameterization(&quaternion(0), - quaternion_parameterization); + problem.SetManifold(&quaternion(0), quaternion_manifold); } } diff --git a/intern/opensubdiv/internal/evaluator/eval_output.h b/intern/opensubdiv/internal/evaluator/eval_output.h index a55b89001a4..9ca09f011a1 100644 --- a/intern/opensubdiv/internal/evaluator/eval_output.h +++ b/intern/opensubdiv/internal/evaluator/eval_output.h @@ -353,15 +353,15 @@ class VolatileEvalOutput : public EvalOutputAPI::EvalOutput { varying_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(varying_stencils, device_context_); // Create evaluators for every face varying channel. - face_varying_evaluators.reserve(all_face_varying_stencils.size()); + face_varying_evaluators_.reserve(all_face_varying_stencils.size()); int face_varying_channel = 0; for (const StencilTable *face_varying_stencils : all_face_varying_stencils) { - face_varying_evaluators.push_back(new FaceVaryingEval(face_varying_channel, - face_varying_stencils, - face_varying_width, - patch_table_, - evaluator_cache_, - device_context_)); + face_varying_evaluators_.push_back(new FaceVaryingEval(face_varying_channel, + face_varying_stencils, + face_varying_width, + patch_table_, + evaluator_cache_, + device_context_)); ++face_varying_channel; } } @@ -373,7 +373,7 @@ class VolatileEvalOutput : public EvalOutputAPI::EvalOutput { delete patch_table_; delete vertex_stencils_; delete varying_stencils_; - for (FaceVaryingEval *face_varying_evaluator : face_varying_evaluators) { + for (FaceVaryingEval *face_varying_evaluator : face_varying_evaluators_) { delete face_varying_evaluator; } } @@ -396,8 +396,8 @@ class VolatileEvalOutput : public EvalOutputAPI::EvalOutput { int num_vertices) override { assert(face_varying_channel >= 0); - assert(face_varying_channel < face_varying_evaluators.size()); - face_varying_evaluators[face_varying_channel]->updateData(src, start_vertex, num_vertices); + assert(face_varying_channel < face_varying_evaluators_.size()); + face_varying_evaluators_[face_varying_channel]->updateData(src, start_vertex, num_vertices); } bool hasVaryingData() const @@ -409,7 +409,7 @@ class VolatileEvalOutput : public EvalOutputAPI::EvalOutput { bool hasFaceVaryingData() const { - return face_varying_evaluators.size() != 0; + return face_varying_evaluators_.size() != 0; } void refine() override @@ -442,7 +442,7 @@ class VolatileEvalOutput : public EvalOutputAPI::EvalOutput { } // Evaluate face-varying data. if (hasFaceVaryingData()) { - for (FaceVaryingEval *face_varying_evaluator : face_varying_evaluators) { + for (FaceVaryingEval *face_varying_evaluator : face_varying_evaluators_) { face_varying_evaluator->refine(); } } @@ -527,8 +527,8 @@ class VolatileEvalOutput : public EvalOutputAPI::EvalOutput { float face_varying[2]) override { assert(face_varying_channel >= 0); - assert(face_varying_channel < face_varying_evaluators.size()); - face_varying_evaluators[face_varying_channel]->evalPatches( + assert(face_varying_channel < face_varying_evaluators_.size()); + face_varying_evaluators_[face_varying_channel]->evalPatches( patch_coord, num_patch_coords, face_varying); } @@ -544,17 +544,17 @@ class VolatileEvalOutput : public EvalOutputAPI::EvalOutput { SRC_VERTEX_BUFFER *getFVarSrcBuffer(const int face_varying_channel) const { - return face_varying_evaluators[face_varying_channel]->getSrcBuffer(); + return face_varying_evaluators_[face_varying_channel]->getSrcBuffer(); } int getFVarSrcBufferOffset(const int face_varying_channel) const { - return face_varying_evaluators[face_varying_channel]->getFVarSrcBufferOffset(); + return face_varying_evaluators_[face_varying_channel]->getFVarSrcBufferOffset(); } PATCH_TABLE *getFVarPatchTable(const int face_varying_channel) const { - return face_varying_evaluators[face_varying_channel]->getPatchTable(); + return face_varying_evaluators_[face_varying_channel]->getPatchTable(); } private: @@ -570,7 +570,7 @@ class VolatileEvalOutput : public EvalOutputAPI::EvalOutput { const STENCIL_TABLE *varying_stencils_; int face_varying_width_; - vector<FaceVaryingEval *> face_varying_evaluators; + vector<FaceVaryingEval *> face_varying_evaluators_; EvaluatorCache *evaluator_cache_; DEVICE_CONTEXT *device_context_; diff --git a/intern/opensubdiv/internal/evaluator/eval_output_cpu.h b/intern/opensubdiv/internal/evaluator/eval_output_cpu.h index 58bae7a322e..42aa052863a 100644 --- a/intern/opensubdiv/internal/evaluator/eval_output_cpu.h +++ b/intern/opensubdiv/internal/evaluator/eval_output_cpu.h @@ -32,7 +32,7 @@ using OpenSubdiv::Osd::CpuVertexBuffer; namespace blender { namespace opensubdiv { -// Note: Define as a class instead of typedef to make it possible +// NOTE: Define as a class instead of typedef to make it possible // to have anonymous class in opensubdiv_evaluator_internal.h class CpuEvalOutput : public VolatileEvalOutput<CpuVertexBuffer, CpuVertexBuffer, diff --git a/intern/rigidbody/RBI_api.h b/intern/rigidbody/RBI_api.h index 791a4a6ac05..13b1c096a80 100644 --- a/intern/rigidbody/RBI_api.h +++ b/intern/rigidbody/RBI_api.h @@ -14,7 +14,7 @@ extern "C" { #endif /* API Notes: - * Currently, this API is optimised for Bullet RigidBodies, and doesn't + * Currently, this API is optimized for Bullet RigidBodies, and doesn't * take into account other Physics Engines. Some tweaking may be necessary * to allow other systems to be used, in particular there may be references * to datatypes that aren't used here... diff --git a/pyproject.toml b/pyproject.toml index 283e4c40db2..22d2d768d08 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,11 +20,6 @@ ignore = [ # Why disable? Re-ordering imports is disruptive and breaks some scripts # that need to check if a module has already been loaded in the case of reloading. "E402", - # Info: Try to make lines fit within --max-line-length characters. - # Why disable? Causes lines to be wrapped, where long lines have the trailing bracket moved to the end of the line. - # If trailing commas were respected as they are by clang-format this might be acceptable. - # Note that this doesn't disable all line wrapping. - "E501", # Info: Fix various deprecated code (via lib2to3) # Why disable? Does nothing besides incorrectly adding a duplicate import, # could be reported as a bug except this is likely to be removed soon, see: diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg index 6f0216176d7..f8164d1f646 100644 --- a/release/datafiles/blender_icons.svg +++ b/release/datafiles/blender_icons.svg @@ -13658,6 +13658,38 @@ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> </g> <g + id="g17058" + transform="translate(-20.839982,-20.882701)" + style="display:inline;enable-background:new"> + <g + id="g7978"> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.928338;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new" + d="m 373.21906,563.36205 c -2.04533,0 -3.71335,1.66802 -3.71335,3.71335 0,2.04534 1.66802,3.71336 3.71335,3.71336 2.04534,0 3.71336,-1.66802 3.71336,-3.71336 0,-2.04533 -1.66802,-3.71335 -3.71336,-3.71335 z" + id="path7726" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sssss" /> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.6;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:922.783;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new" + d="m 364.3828,556.88974 c -0.8999,0 -1.63379,0.73389 -1.63379,1.6338 0,0.89988 0.73389,1.6338 1.63379,1.6338 0.89991,0 1.6338,-0.7339 1.6338,-1.6338 0,-0.8999 -0.73389,-1.6338 -1.6338,-1.6338 z" + id="path7808" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sssss" /> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.85;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:922.783;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new" + d="m 366.50464,561.82783 c -1.31395,0 -2.38552,1.07155 -2.38552,2.3855 0,1.31395 1.07157,2.38555 2.38552,2.38555 1.31396,0 2.38553,-1.07158 2.38553,-2.38555 0,-1.31395 -1.07157,-2.3855 -2.38553,-2.3855 z" + id="path7890" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sssss" /> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.6;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1881.46;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new" + d="m 368.88012,557.69285 c -1.06196,0 -1.928,0.86602 -1.928,1.92801 0,1.06192 0.86604,1.92798 1.928,1.92798 1.06195,0 1.92801,-0.86606 1.92801,-1.92798 0,-1.06195 -0.86606,-1.92801 -1.92801,-1.92801 z" + id="path7972" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sssss" /> + </g> + </g> + <g id="g7662" style="fill:#ffffff" transform="matrix(1,0,0,-1,0,1085.9844)"> diff --git a/release/datafiles/blender_icons16/icon16_geometry_nodes.dat b/release/datafiles/blender_icons16/icon16_geometry_nodes.dat Binary files differnew file mode 100644 index 00000000000..195a5b9d6d9 --- /dev/null +++ b/release/datafiles/blender_icons16/icon16_geometry_nodes.dat diff --git a/release/datafiles/blender_icons32/icon32_geometry_nodes.dat b/release/datafiles/blender_icons32/icon32_geometry_nodes.dat Binary files differnew file mode 100644 index 00000000000..50971291fcc --- /dev/null +++ b/release/datafiles/blender_icons32/icon32_geometry_nodes.dat diff --git a/release/datafiles/icons/ops.curves.sculpt_puff.dat b/release/datafiles/icons/ops.curves.sculpt_puff.dat Binary files differnew file mode 100644 index 00000000000..db2bab46bfe --- /dev/null +++ b/release/datafiles/icons/ops.curves.sculpt_puff.dat diff --git a/release/datafiles/icons/ops.sculpt.box_trim.dat b/release/datafiles/icons/ops.sculpt.box_trim.dat Binary files differindex 9a2e691a621..558c2b162ac 100644 --- a/release/datafiles/icons/ops.sculpt.box_trim.dat +++ b/release/datafiles/icons/ops.sculpt.box_trim.dat diff --git a/release/datafiles/icons/ops.sculpt.lasso_trim.dat b/release/datafiles/icons/ops.sculpt.lasso_trim.dat Binary files differindex e87cdb97fec..60c33fc9f2f 100644 --- a/release/datafiles/icons/ops.sculpt.lasso_trim.dat +++ b/release/datafiles/icons/ops.sculpt.lasso_trim.dat diff --git a/release/datafiles/splash.png b/release/datafiles/splash.png Binary files differindex 332ee39849e..eb1250cf5a5 100644 --- a/release/datafiles/splash.png +++ b/release/datafiles/splash.png diff --git a/release/scripts/modules/bpy_extras/__init__.py b/release/scripts/modules/bpy_extras/__init__.py index 1af9048ebfd..15a8d00cddc 100644 --- a/release/scripts/modules/bpy_extras/__init__.py +++ b/release/scripts/modules/bpy_extras/__init__.py @@ -16,4 +16,5 @@ __all__ = ( "mesh_utils", "node_utils", "view3d_utils", + "id_map_utils", ) diff --git a/release/scripts/modules/bpy_extras/id_map_utils.py b/release/scripts/modules/bpy_extras/id_map_utils.py new file mode 100644 index 00000000000..cf39f2185c6 --- /dev/null +++ b/release/scripts/modules/bpy_extras/id_map_utils.py @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# <pep8 compliant> + +from typing import Dict, Set +import bpy +from bpy.types import ID + + +__all__ = ( + "get_id_reference_map", + "get_all_referenced_ids", +) + + +def get_id_reference_map() -> Dict[ID, Set[ID]]: + """Return a dictionary of direct datablock references for every datablock in the blend file.""" + inv_map = {} + for key, values in bpy.data.user_map().items(): + for value in values: + if value == key: + # So an object is not considered to be referencing itself. + continue + inv_map.setdefault(value, set()).add(key) + return inv_map + + +def recursive_get_referenced_ids( + ref_map: Dict[ID, Set[ID]], id: ID, referenced_ids: Set, visited: Set +): + """Recursively populate referenced_ids with IDs referenced by id.""" + if id in visited: + # Avoid infinite recursion from circular references. + return + visited.add(id) + for ref in ref_map.get(id, []): + referenced_ids.add(ref) + recursive_get_referenced_ids( + ref_map=ref_map, id=ref, referenced_ids=referenced_ids, visited=visited + ) + + +def get_all_referenced_ids(id: ID, ref_map: Dict[ID, Set[ID]]) -> Set[ID]: + """Return a set of IDs directly or indirectly referenced by id.""" + referenced_ids = set() + recursive_get_referenced_ids( + ref_map=ref_map, id=id, referenced_ids=referenced_ids, visited=set() + ) + return referenced_ids diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index b3f0c055769..4c6e2508859 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -1145,3 +1145,11 @@ class TextureNode(NodeInternal): @classmethod def poll(cls, ntree): return ntree.bl_idname == 'TextureNodeTree' + + +class GeometryNode(NodeInternal): + __slots__ = () + + @classmethod + def poll(cls, ntree): + return ntree.bl_idname == 'GeometryNodeTree' diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 64e0917da65..3e0d3dadcf1 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1300,6 +1300,8 @@ def km_uv_editor(params): {"properties": [("data_path", 'tool_settings.use_snap_uv')]}), ("wm.context_menu_enum", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True}, {"properties": [("data_path", 'tool_settings.snap_uv_element')]}), + ("wm.context_toggle", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "ctrl": True}, + {"properties": [("data_path", 'space_data.show_gizmo')]}), *_template_items_context_menu("IMAGE_MT_uvs_context_menu", params.context_menu_event), ]) @@ -1967,6 +1969,8 @@ def km_image(params): ), ("image.render_border", {"type": 'B', "value": 'PRESS', "ctrl": True}, None), ("image.clear_render_border", {"type": 'B', "value": 'PRESS', "ctrl": True, "alt": True}, None), + ("wm.context_toggle", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "ctrl": True}, + {"properties": [("data_path", 'space_data.show_gizmo')]}), *_template_items_context_menu("IMAGE_MT_mask_context_menu", params.context_menu_event), ]) @@ -3960,6 +3964,8 @@ def km_grease_pencil_stroke_sculpt_mode(params): ("gpencil.active_frames_delete_all", {"type": 'DEL', "value": 'PRESS', "shift": True}, None), # Active layer op_menu("GPENCIL_MT_layer_active", {"type": 'Y', "value": 'PRESS'}), + # Active material + op_menu("GPENCIL_MT_material_active", {"type": 'U', "value": 'PRESS'}), # Merge Layer ("gpencil.layer_merge", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None), # Keyframe menu diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py index 6857670c31d..0b146d689f5 100644 --- a/release/scripts/startup/bl_operators/object.py +++ b/release/scripts/startup/bl_operators/object.py @@ -848,10 +848,6 @@ class DupliOffsetFromCursor(Operator): bl_label = "Set Offset from Cursor" bl_options = {'INTERNAL', 'UNDO'} - @classmethod - def poll(cls, context): - return (context.active_object is not None) - def execute(self, context): scene = context.scene collection = context.collection diff --git a/release/scripts/startup/bl_ui/properties_data_curve.py b/release/scripts/startup/bl_ui/properties_data_curve.py index d6e21053e3b..a421428c067 100644 --- a/release/scripts/startup/bl_ui/properties_data_curve.py +++ b/release/scripts/startup/bl_ui/properties_data_curve.py @@ -203,7 +203,7 @@ class DATA_PT_geometry_curve_start_end(CurveButtonsPanelCurve, Panel): @classmethod def poll(cls, context): # Text objects don't support these properties - return (type(context.curve) in {Curve}) + return (type(context.curve) == Curve) def draw(self, context): layout = self.layout diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 481753d5e79..6b55683ee89 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -41,17 +41,7 @@ class AnnotationDrawingToolsPanel: row.prop_enum(tool_settings, "annotation_stroke_placement_view2d", 'IMAGE', text="Image") -class GreasePencilSculptOptionsPanel: - bl_label = "Sculpt Strokes" - - @classmethod - def poll(cls, context): - tool_settings = context.scene.tool_settings - settings = tool_settings.gpencil_sculpt_paint - brush = settings.brush - tool = brush.gpencil_sculpt_tool - - return bool(tool in {'SMOOTH', 'RANDOMIZE'}) +class GreasePencilSculptAdvancedPanel: def draw(self, context): layout = self.layout @@ -59,17 +49,21 @@ class GreasePencilSculptOptionsPanel: layout.use_property_decorate = False tool_settings = context.scene.tool_settings - settings = tool_settings.gpencil_sculpt_paint - brush = settings.brush - gp_settings = brush.gpencil_settings + brush = tool_settings.gpencil_sculpt_paint.brush tool = brush.gpencil_sculpt_tool + gp_settings = brush.gpencil_settings - if tool in {'SMOOTH', 'RANDOMIZE'}: - layout.prop(gp_settings, "use_edit_position", text="Affect Position") - layout.prop(gp_settings, "use_edit_strength", text="Affect Strength") - layout.prop(gp_settings, "use_edit_thickness", text="Affect Thickness") + col = layout.column(heading="Auto-Masking", align=True) + col.prop(gp_settings, "use_automasking_stroke", text="Stroke") + col.prop(gp_settings, "use_automasking_layer", text="Layer") + col.prop(gp_settings, "use_automasking_material", text="Material") - layout.prop(gp_settings, "use_edit_uv", text="Affect UV") + if tool in {'SMOOTH', 'RANDOMIZE'}: + col = layout.column(heading="Affect", align=True) + col.prop(gp_settings, "use_edit_position", text="Position") + col.prop(gp_settings, "use_edit_strength", text="Strength") + col.prop(gp_settings, "use_edit_thickness", text="Thickness") + col.prop(gp_settings, "use_edit_uv", text="UV") # GP Object Tool Settings diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 9a116aa1717..d5b106635ab 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -405,7 +405,13 @@ class FalloffPanel(BrushPanel): if not super().poll(context): return False settings = cls.paint_settings(context) - return (settings and settings.brush and settings.brush.curve) + if not (settings and settings.brush and settings.brush.curve): + return False + if cls.get_brush_mode(context) == 'SCULPT_CURVES': + brush = settings.brush + if brush.curves_sculpt_tool in {'ADD', 'DELETE'}: + return False + return True def draw(self, context): layout = self.layout @@ -432,7 +438,13 @@ class FalloffPanel(BrushPanel): row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE' row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX' - if mode in {'SCULPT', 'PAINT_VERTEX', 'PAINT_WEIGHT', 'SCULPT_CURVES'} and brush.sculpt_tool != 'POSE': + show_fallof_shape = False + if mode in {'SCULPT', 'PAINT_VERTEX', 'PAINT_WEIGHT'} and brush.sculpt_tool != 'POSE': + show_fallof_shape = True + if not show_fallof_shape and mode == 'SCULPT_CURVES' and context.space_data.type == 'PROPERTIES': + show_fallof_shape = True + + if show_fallof_shape: col.separator() row = col.row(align=True) row.use_property_split = True @@ -769,6 +781,26 @@ def brush_settings(layout, context, brush, popover=False): elif brush.color_type == 'GRADIENT': layout.row().prop(brush, "gradient_fill_mode", expand=True) + elif mode == 'SCULPT_CURVES': + if brush.curves_sculpt_tool == 'ADD': + layout.prop(brush.curves_sculpt_settings, "add_amount") + layout.prop(brush.curves_sculpt_settings, "curve_length") + col = layout.column(heading="Interpolate", align=True) + col.prop(brush.curves_sculpt_settings, "interpolate_length", text="Length") + col.prop(brush.curves_sculpt_settings, "interpolate_shape", text="Shape") + col.prop(brush.curves_sculpt_settings, "interpolate_point_count", text="Point Count") + + col = layout.column() + col.active = not brush.curves_sculpt_settings.interpolate_length + col.prop(brush.curves_sculpt_settings, "curve_length") + + col = layout.column() + col.active = not brush.curves_sculpt_settings.interpolate_point_count + col.prop(brush.curves_sculpt_settings, "points_per_curve") + elif brush.curves_sculpt_tool == 'GROW_SHRINK': + layout.prop(brush.curves_sculpt_settings, "scale_uniform") + layout.prop(brush.curves_sculpt_settings, "minimum_length") + def brush_shared_settings(layout, context, brush, popover=False): """ Draw simple brush settings that are shared between different paint modes. """ @@ -827,6 +859,7 @@ def brush_shared_settings(layout, context, brush, popover=False): if mode == 'SCULPT_CURVES': size = True strength = True + direction = brush.curves_sculpt_tool == 'GROW_SHRINK' ### Draw settings. ### ups = context.scene.tool_settings.unified_paint_settings @@ -925,16 +958,6 @@ def brush_settings_advanced(layout, context, brush, popover=False): col.prop(brush, "use_original_plane", text="Plane") layout.separator() - elif mode == 'SCULPT_CURVES': - if brush.curves_sculpt_tool == 'ADD': - layout.prop(brush.curves_sculpt_settings, "add_amount") - layout.prop(brush.curves_sculpt_settings, "curve_length") - layout.prop(brush.curves_sculpt_settings, "interpolate_length") - layout.prop(brush.curves_sculpt_settings, "interpolate_shape") - elif brush.curves_sculpt_tool == 'GROW_SHRINK': - layout.prop(brush.curves_sculpt_settings, "scale_uniform") - layout.prop(brush.curves_sculpt_settings, "minimum_length") - # 3D and 2D Texture Paint. elif mode in {'PAINT_TEXTURE', 'PAINT_2D'}: capabilities = brush.image_paint_capabilities diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py index 0b7a728fa95..3d6ad2ece9b 100644 --- a/release/scripts/startup/bl_ui/properties_particle.py +++ b/release/scripts/startup/bl_ui/properties_particle.py @@ -54,8 +54,10 @@ class PARTICLE_MT_context_menu(Menu): bl_label = "Particle Specials" COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - def draw(self, _context): + def draw(self, context): layout = self.layout + psys = context.particle_system + experimental = context.preferences.experimental props = layout.operator( "particle.copy_particle_systems", @@ -72,6 +74,11 @@ class PARTICLE_MT_context_menu(Menu): props.use_active = False props.remove_target_particles = True + if experimental.use_new_curves_type and psys.settings.type == 'HAIR': + layout.operator( + "curves.convert_from_particle_system", + text="Convert to Curves") + layout.separator() layout.operator( diff --git a/release/scripts/startup/bl_ui/properties_physics_field.py b/release/scripts/startup/bl_ui/properties_physics_field.py index c1b213766df..2e7e68f02ef 100644 --- a/release/scripts/startup/bl_ui/properties_physics_field.py +++ b/release/scripts/startup/bl_ui/properties_physics_field.py @@ -77,7 +77,6 @@ class PHYSICS_PT_field_settings(PhysicButtonsPanel, Panel): elif field.type == 'GUIDE': col = flow.column() - col.prop(field, "guide_minimum") col.prop(field, "guide_free") col.prop(field, "falloff_power") col.prop(field, "use_guide_path_add") @@ -88,11 +87,20 @@ class PHYSICS_PT_field_settings(PhysicButtonsPanel, Panel): col = flow.column() col.prop(field, "guide_clump_amount", text="Clumping Amount") col.prop(field, "guide_clump_shape") - col.prop(field, "use_max_distance") - sub = col.column() + col.separator() + + col.prop(field, "guide_minimum", text="Min Distance") + + col = layout.column(align=False, heading="Max Distance") + col.use_property_decorate = False + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(field, "use_max_distance", text="") + sub = sub.row(align=True) sub.active = field.use_max_distance - sub.prop(field, "distance_max") + sub.prop(field, "distance_max", text="") + row.prop_decorator(field, "distance_max") elif field.type == 'TEXTURE': col = flow.column() diff --git a/release/scripts/startup/bl_ui/properties_texture.py b/release/scripts/startup/bl_ui/properties_texture.py index 5ceaf8fc644..1f9362f02b5 100644 --- a/release/scripts/startup/bl_ui/properties_texture.py +++ b/release/scripts/startup/bl_ui/properties_texture.py @@ -840,7 +840,6 @@ class TEXTURE_PT_colors_ramp(TextureButtonsPanel, TextureColorsPoll, Panel): if is_active: layout.template_color_ramp(tex, "color_ramp", expand=True) else: - layout.alignment = 'RIGHT' layout.label(text="Enable the Color Ramp first") diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 1dd50c979e2..d61055c9024 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -806,6 +806,13 @@ class IMAGE_HT_header(Header): layout.separator_spacer() + # Gizmo toggle & popover. + row = layout.row(align=True) + row.prop(sima, "show_gizmo", icon='GIZMO', text="") + sub = row.row(align=True) + sub.active = sima.show_gizmo + sub.popover(panel="IMAGE_PT_gizmo_display", text="") + # Overlay toggle & popover row = layout.row(align=True) row.prop(overlay, "show_overlays", icon='OVERLAY', text="") @@ -1453,6 +1460,26 @@ class IMAGE_PT_uv_cursor(Panel): col.prop(sima, "cursor_location", text="Location") +class IMAGE_PT_gizmo_display(Panel): + bl_space_type = 'IMAGE_EDITOR' + bl_region_type = 'HEADER' + bl_label = "Gizmos" + bl_ui_units_x = 8 + + def draw(self, context): + layout = self.layout + + view = context.space_data + + col = layout.column() + col.label(text="Viewport Gizmos") + col.separator() + + col.active = view.show_gizmo + colsub = col.column() + colsub.prop(view, "show_gizmo_navigate", text="Navigate") + + class IMAGE_PT_overlay(Panel): bl_space_type = 'IMAGE_EDITOR' bl_region_type = 'HEADER' @@ -1680,6 +1707,7 @@ classes = ( IMAGE_PT_scope_sample, IMAGE_PT_uv_cursor, IMAGE_PT_annotation, + IMAGE_PT_gizmo_display, IMAGE_PT_overlay, IMAGE_PT_overlay_guides, IMAGE_PT_overlay_uv_edit, diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 7c7bb456473..94bad5459c9 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -611,7 +611,16 @@ class SEQUENCER_MT_change(Menu): strip = context.active_sequence_strip layout.operator_context = 'INVOKE_REGION_WIN' + if strip and strip.type == 'SCENE': + bpy_data_scenes_len = len(bpy.data.scenes) + if bpy_data_scenes_len > 10: + layout.operator_context = 'INVOKE_DEFAULT' + layout.operator("sequencer.change_scene", text="Change Scene...") + elif bpy_data_scenes_len > 1: + layout.operator_menu_enum("sequencer.change_scene", "scene", text="Change Scene") + del bpy_data_scenes_len + layout.operator_context = 'INVOKE_DEFAULT' layout.operator_menu_enum("sequencer.change_effect_input", "swap") layout.operator_menu_enum("sequencer.change_effect_type", "type") prop = layout.operator("sequencer.change_path", text="Path/Files") @@ -667,15 +676,7 @@ class SEQUENCER_MT_add(Menu): layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' - bpy_data_scenes_len = len(bpy.data.scenes) - if bpy_data_scenes_len > 10: - layout.operator_context = 'INVOKE_DEFAULT' - layout.operator("sequencer.scene_strip_add", text="Scene...", icon='SCENE_DATA') - elif bpy_data_scenes_len > 1: - layout.operator_menu_enum("sequencer.scene_strip_add", "scene", text="Scene", icon='SCENE_DATA') - else: - layout.menu("SEQUENCER_MT_add_empty", text="Scene", icon='SCENE_DATA') - del bpy_data_scenes_len + layout.menu("SEQUENCER_MT_add_scene", text="Scene", icon='SCENE_DATA') bpy_data_movieclips_len = len(bpy.data.movieclips) if bpy_data_movieclips_len > 10: @@ -725,6 +726,34 @@ class SEQUENCER_MT_add(Menu): col.enabled = selected_sequences_len(context) >= 1 +class SEQUENCER_MT_add_scene(Menu): + bl_label = "Scene" + bl_translation_context = i18n_contexts.operator_default + + def draw(self, context): + + layout = self.layout + layout.operator_context = 'INVOKE_REGION_WIN' + layout.operator("sequencer.scene_strip_add_new", text="New Scene", icon="ADD").type = 'NEW' + + bpy_data_scenes_len = len(bpy.data.scenes) + if bpy_data_scenes_len > 10: + layout.separator() + layout.operator_context = 'INVOKE_DEFAULT' + layout.operator("sequencer.scene_strip_add", text="Scene...", icon='SCENE_DATA') + elif bpy_data_scenes_len > 1: + layout.separator() + scene = context.scene + for sc_item in bpy.data.scenes: + if sc_item == scene: + continue + + layout.operator_context = 'INVOKE_REGION_WIN' + layout.operator("sequencer.scene_strip_add", text=sc_item.name).scene = sc_item.name + + del bpy_data_scenes_len + + class SEQUENCER_MT_add_empty(Menu): bl_label = "Empty" @@ -917,6 +946,9 @@ class SEQUENCER_MT_strip(Menu): strip = context.active_sequence_strip + if strip and strip.type == 'SCENE': + layout.operator("sequencer.delete", text="Delete Strip & Data").delete_data = True + if has_sequencer: if strip: strip_type = strip.type @@ -1035,6 +1067,10 @@ class SEQUENCER_MT_context_menu(Menu): props.keep_open = False layout.operator("sequencer.delete", text="Delete") + strip = context.active_sequence_strip + if strip and strip.type == 'SCENE': + layout.operator("sequencer.delete", text="Delete Strip & Data").delete_data = True + layout.separator() layout.operator("sequencer.slip", text="Slip Strip Contents") @@ -2600,6 +2636,7 @@ classes = ( SEQUENCER_MT_marker, SEQUENCER_MT_navigation, SEQUENCER_MT_add, + SEQUENCER_MT_add_scene, SEQUENCER_MT_add_effect, SEQUENCER_MT_add_transitions, SEQUENCER_MT_add_empty, diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index af56b966f17..ee3e7b7df8f 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2260,7 +2260,6 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel): self._draw_items( context, ( ({"property": "use_sculpt_tools_tilt"}, "T82877"), - ({"property": "use_sculpt_texture_paint"}, "T96225"), ({"property": "use_extended_asset_browser"}, ("project/view/130/", "Project Page")), ({"property": "use_override_templates"}, ("T73318", "Milestone 4")), ), @@ -2274,7 +2273,9 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): self._draw_items( context, ( ({"property": "use_new_curves_type"}, "T68981"), + ({"property": "use_new_curves_tools"}, "T68981"), ({"property": "use_new_point_cloud_type"}, "T75717"), + ({"property": "use_sculpt_texture_paint"}, "T96225"), ({"property": "use_full_frame_compositor"}, "T88150"), ({"property": "enable_eevee_next"}, "T93220"), ({"property": "use_draw_manager_acquire_lock"}, "T98016"), diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index a8624030564..bcc5976fb59 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -109,8 +109,8 @@ class VIEW3D_HT_tool_header(Header): if is_valid_context: brush = context.tool_settings.gpencil_sculpt_paint.brush tool = brush.gpencil_sculpt_tool - if tool in {'SMOOTH', 'RANDOMIZE'}: - layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_options") + if tool != 'CLONE': + layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_brush_popover") layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_appearance") elif tool_mode == 'WEIGHT_GPENCIL': if is_valid_context: @@ -151,6 +151,11 @@ class VIEW3D_HT_tool_header(Header): row.popover(panel="VIEW3D_PT_sculpt_symmetry_for_topbar", text="") elif mode_string == 'PAINT_VERTEX': row.popover(panel="VIEW3D_PT_tools_vertexpaint_symmetry_for_topbar", text="") + elif mode_string == 'SCULPT_CURVES': + _row, sub = row_for_mirror() + sub.prop(context.object.data, "use_mirror_x", text="X", toggle=True) + sub.prop(context.object.data, "use_mirror_y", text="Y", toggle=True) + sub.prop(context.object.data, "use_mirror_z", text="Z", toggle=True) # Expand panels from the side-bar as popovers. popover_kw = {"space_type": 'VIEW_3D', "region_type": 'UI', "category": "Tool"} @@ -241,7 +246,7 @@ class _draw_tool_settings_context_mode: unified_name="use_unified_size", text="Radius", slider=True, - header=True + header=True, ) # strength, use_strength_pressure @@ -254,7 +259,7 @@ class _draw_tool_settings_context_mode: pressure_name=pressure_name, unified_name="use_unified_strength", text="Strength", - header=True + header=True, ) # direction @@ -315,7 +320,7 @@ class _draw_tool_settings_context_mode: "weight", unified_name="use_unified_weight", slider=True, - header=True + header=True, ) UnifiedPaintPanel.prop_unified( @@ -327,7 +332,7 @@ class _draw_tool_settings_context_mode: unified_name="use_unified_size", slider=True, text="Radius", - header=True + header=True, ) UnifiedPaintPanel.prop_unified( layout, @@ -336,7 +341,7 @@ class _draw_tool_settings_context_mode: "strength", pressure_name="use_pressure_strength", unified_name="use_unified_strength", - header=True + header=True, ) return True @@ -479,7 +484,6 @@ class _draw_tool_settings_context_mode: tool_settings = context.tool_settings paint = tool_settings.curves_sculpt - layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) brush = paint.brush if brush is None: @@ -491,9 +495,10 @@ class _draw_tool_settings_context_mode: brush, "size", unified_name="use_unified_size", + pressure_name="use_pressure_size", text="Radius", slider=True, - header=True + header=True, ) if brush.curves_sculpt_tool not in {'ADD', 'DELETE'}: @@ -503,31 +508,29 @@ class _draw_tool_settings_context_mode: brush, "strength", unified_name="use_unified_strength", - header=True + pressure_name="use_pressure_strength", + header=True, ) if brush.curves_sculpt_tool == 'COMB': layout.prop(brush, "falloff_shape", expand=True) - layout.prop(brush, "curve_preset") + layout.popover("VIEW3D_PT_tools_brush_falloff") if brush.curves_sculpt_tool == 'ADD': - layout.prop(brush, "use_frontface") layout.prop(brush, "falloff_shape", expand=True) layout.prop(brush.curves_sculpt_settings, "add_amount") - layout.prop(brush.curves_sculpt_settings, "curve_length") - layout.prop(brush.curves_sculpt_settings, "interpolate_length") - layout.prop(brush.curves_sculpt_settings, "interpolate_shape") + layout.popover("VIEW3D_PT_curves_sculpt_add_shape", text="Curve Shape") + layout.prop(brush, "use_frontface", text="Front Faces Only") if brush.curves_sculpt_tool == 'GROW_SHRINK': layout.prop(brush, "direction", expand=True, text="") layout.prop(brush, "falloff_shape", expand=True) - layout.prop(brush.curves_sculpt_settings, "scale_uniform") - layout.prop(brush.curves_sculpt_settings, "minimum_length") - layout.prop(brush, "curve_preset") + layout.popover("VIEW3D_PT_curves_sculpt_grow_shrink_scaling", text="Scaling") + layout.popover("VIEW3D_PT_tools_brush_falloff") if brush.curves_sculpt_tool == 'SNAKE_HOOK': layout.prop(brush, "falloff_shape", expand=True) - layout.prop(brush, "curve_preset") + layout.popover("VIEW3D_PT_tools_brush_falloff") if brush.curves_sculpt_tool == 'DELETE': layout.prop(brush, "falloff_shape", expand=True) @@ -2035,7 +2038,7 @@ class VIEW3D_MT_curve_add(Menu): bl_idname = "VIEW3D_MT_curve_add" bl_label = "Curve" - def draw(self, _context): + def draw(self, context): layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' @@ -2049,18 +2052,14 @@ class VIEW3D_MT_curve_add(Menu): layout.operator("curve.primitive_nurbs_circle_add", text="Nurbs Circle", icon='CURVE_NCIRCLE') layout.operator("curve.primitive_nurbs_path_add", text="Path", icon='CURVE_PATH') + experimental = context.preferences.experimental + if experimental.use_new_curves_type: + layout.separator() -class VIEW3D_MT_curves_add(Menu): - bl_idname = "VIEW3D_MT_curves_add" - bl_label = "Curves" - - def draw(self, _context): - layout = self.layout - - layout.operator_context = 'INVOKE_REGION_WIN' + layout.operator("object.curves_empty_hair_add", text="Empty Hair", icon='CURVES_DATA') - layout.operator("object.curves_empty_hair_add", text="Empty Hair", icon='CURVES_DATA') - layout.operator("object.curves_random_add", text="Random", icon='CURVES_DATA') + if experimental.use_new_curves_tools: + layout.operator("object.curves_random_add", text="Random", icon='CURVES_DATA') class VIEW3D_MT_surface_add(Menu): @@ -2215,8 +2214,6 @@ class VIEW3D_MT_add(Menu): # layout.operator_menu_enum("object.curve_add", "type", text="Curve", icon='OUTLINER_OB_CURVE') layout.menu("VIEW3D_MT_curve_add", icon='OUTLINER_OB_CURVE') - if context.preferences.experimental.use_new_curves_type: - layout.menu("VIEW3D_MT_curves_add", icon='OUTLINER_OB_CURVES') # layout.operator_menu_enum("object.surface_add", "type", text="Surface", icon='OUTLINER_OB_SURFACE') layout.menu("VIEW3D_MT_surface_add", icon='OUTLINER_OB_SURFACE') layout.menu("VIEW3D_MT_metaball_add", text="Metaball", icon='OUTLINER_OB_META') @@ -2345,6 +2342,7 @@ class VIEW3D_MT_object(Menu): layout.separator() layout.operator("object.shade_smooth") + layout.operator("object.shade_smooth", text="Shade Auto Smooth").use_auto_smooth = True layout.operator("object.shade_flat") layout.separator() @@ -2587,7 +2585,8 @@ class VIEW3D_MT_object_context_menu(Menu): # Shared among some object types. if obj is not None: if obj.type in {'MESH', 'CURVE', 'SURFACE'}: - layout.operator("object.shade_smooth", text="Shade Smooth") + layout.operator("object.shade_smooth") + layout.operator("object.shade_smooth", text="Shade Auto Smooth").use_auto_smooth = True layout.operator("object.shade_flat", text="Shade Flat") layout.separator() @@ -3147,7 +3146,9 @@ class VIEW3D_MT_sculpt_curves(Menu): def draw(self, _context): layout = self.layout - layout.operator("curves.snap_curves_to_surface") + layout.operator("curves.snap_curves_to_surface", text="Snap to Deformed Surface").attach_mode = 'DEFORM' + layout.operator("curves.snap_curves_to_surface", text="Snap to Nearest Surface").attach_mode = 'NEAREST' + layout.separator() layout.operator("curves.convert_to_particle_system", text="Convert to Particle System") @@ -5688,7 +5689,7 @@ class VIEW3D_PT_object_type_visibility(Panel): # Allows derived classes to pass view data other than context.space_data. # This is used by the official VR add-on, which passes XrSessionSettings # since VR has a 3D view that only exists for the duration of the VR session. - def draw_ex(self, context, view, show_select): + def draw_ex(self, _context, view, show_select): layout = self.layout layout.use_property_split = True layout.use_property_decorate = False @@ -7612,6 +7613,55 @@ class TOPBAR_PT_gpencil_vertexcolor(GreasePencilVertexcolorPanel, Panel): return ob and ob.type == 'GPENCIL' +class VIEW3D_PT_curves_sculpt_add_shape(Panel): + # Only for popover, these are dummy values. + bl_space_type = 'VIEW_3D' + bl_region_type = 'WINDOW' + bl_label = "Curves Sculpt Add Curve Options" + + def draw(self, context): + layout = self.layout + + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + settings = UnifiedPaintPanel.paint_settings(context) + brush = settings.brush + + col = layout.column(heading="Interpolate", align=True) + col.prop(brush.curves_sculpt_settings, "interpolate_length", text="Length") + col.prop(brush.curves_sculpt_settings, "interpolate_shape", text="Shape") + col.prop(brush.curves_sculpt_settings, "interpolate_point_count", text="Point Count") + + col = layout.column() + col.active = not brush.curves_sculpt_settings.interpolate_length + col.prop(brush.curves_sculpt_settings, "curve_length", text="Length") + + col = layout.column() + col.active = not brush.curves_sculpt_settings.interpolate_point_count + col.prop(brush.curves_sculpt_settings, "points_per_curve", text="Points") + + +class VIEW3D_PT_curves_sculpt_grow_shrink_scaling(Panel): + # Only for popover, these are dummy values. + bl_space_type = 'VIEW_3D' + bl_region_type = 'WINDOW' + bl_label = "Curves Grow/Shrink Scaling" + bl_ui_units_x = 12 + + def draw(self, context): + layout = self.layout + + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + settings = UnifiedPaintPanel.paint_settings(context) + brush = settings.brush + + layout.prop(brush.curves_sculpt_settings, "scale_uniform") + layout.prop(brush.curves_sculpt_settings, "minimum_length") + + classes = ( VIEW3D_HT_header, VIEW3D_HT_tool_header, @@ -7654,7 +7704,6 @@ classes = ( VIEW3D_MT_angle_control, VIEW3D_MT_mesh_add, VIEW3D_MT_curve_add, - VIEW3D_MT_curves_add, VIEW3D_MT_surface_add, VIEW3D_MT_edit_metaball_context_menu, VIEW3D_MT_metaball_add, @@ -7844,6 +7893,8 @@ classes = ( TOPBAR_PT_gpencil_materials, TOPBAR_PT_gpencil_vertexcolor, TOPBAR_PT_annotation_layers, + VIEW3D_PT_curves_sculpt_add_shape, + VIEW3D_PT_curves_sculpt_grow_shrink_scaling, ) diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 570d7c12e30..1b714a40d03 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -3,7 +3,7 @@ # <pep8 compliant> from bpy.types import Menu, Panel, UIList from bl_ui.properties_grease_pencil_common import ( - GreasePencilSculptOptionsPanel, + GreasePencilSculptAdvancedPanel, GreasePencilDisplayPanel, GreasePencilBrushFalloff, ) @@ -276,7 +276,7 @@ class VIEW3D_PT_tools_posemode_options(View3DPanel, Panel): class TEXTURE_UL_texpaintslots(UIList): - def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): + def draw_item(self, _context, layout, _data, item, _icon, _active_data, _active_propname, _index): # mat = data if self.layout_type in {'DEFAULT', 'COMPACT'}: @@ -391,6 +391,11 @@ class VIEW3D_PT_tools_brush_settings_advanced(Panel, View3DPaintBrushPanel): bl_options = {'DEFAULT_CLOSED'} bl_ui_units_x = 14 + @classmethod + def poll(cls, context): + mode = cls.get_brush_mode(context) + return mode is not None and mode != 'SCULPT_CURVES' + def draw(self, context): layout = self.layout @@ -1049,6 +1054,36 @@ class VIEW3D_PT_sculpt_symmetry_for_topbar(Panel): draw = VIEW3D_PT_sculpt_symmetry.draw +class VIEW3D_PT_curves_sculpt_symmetry(Panel, View3DPaintPanel): + bl_context = ".curves_sculpt" # dot on purpose (access from topbar) + bl_label = "Symmetry" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + return context.object and context.object.type == 'CURVES' + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + curves = context.object.data + + row = layout.row(align=True, heading="Mirror") + row.prop(curves, "use_mirror_x", text="X", toggle=True) + row.prop(curves, "use_mirror_y", text="Y", toggle=True) + row.prop(curves, "use_mirror_z", text="Z", toggle=True) + + +class VIEW3D_PT_curves_sculpt_symmetry_for_topbar(Panel): + bl_space_type = 'TOPBAR' + bl_region_type = 'HEADER' + bl_label = "Symmetry" + + draw = VIEW3D_PT_curves_sculpt_symmetry.draw + + # ********** default tools for weight-paint **************** @@ -1907,6 +1942,41 @@ class VIEW3D_PT_tools_grease_pencil_brush_sculpt_falloff(GreasePencilBrushFallof return (settings and settings.brush and settings.brush.curve) +class VIEW3D_PT_tools_grease_pencil_sculpt_brush_advanced(GreasePencilSculptAdvancedPanel, View3DPanel, Panel): + bl_context = ".greasepencil_sculpt" + bl_label = "Advanced" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_sculpt_settings' + bl_category = "Tool" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + brush = context.tool_settings.gpencil_sculpt_paint.brush + if brush is None: + return False + + tool = brush.gpencil_sculpt_tool + return tool != 'CLONE' + + +class VIEW3D_PT_tools_grease_pencil_sculpt_brush_popover(GreasePencilSculptAdvancedPanel, View3DPanel, Panel): + bl_context = ".greasepencil_sculpt" + bl_label = "Brush" + bl_category = "Tool" + + @classmethod + def poll(cls, context): + if context.region.type != 'TOOL_HEADER': + return False + + brush = context.tool_settings.gpencil_sculpt_paint.brush + if brush is None: + return False + + tool = brush.gpencil_sculpt_tool + return tool != 'CLONE' + + # Grease Pencil weight painting tools class GreasePencilWeightPanel: bl_context = ".greasepencil_weight" @@ -2240,13 +2310,6 @@ class VIEW3D_PT_tools_grease_pencil_brush_mix_palette(View3DPanel, Panel): col.template_palette(settings, "palette", color=True) -class VIEW3D_PT_tools_grease_pencil_sculpt_options(GreasePencilSculptOptionsPanel, Panel, View3DPanel): - bl_context = ".greasepencil_sculpt" - bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_sculpt_settings' - bl_category = "Tool" - bl_label = "Sculpt Strokes" - - # Grease Pencil Brush Appearance (one for each mode) class VIEW3D_PT_tools_grease_pencil_paint_appearance(GreasePencilDisplayPanel, Panel, View3DPanel): bl_context = ".greasepencil_paint" @@ -2323,6 +2386,9 @@ classes = ( VIEW3D_PT_sculpt_options, VIEW3D_PT_sculpt_options_gravity, + VIEW3D_PT_curves_sculpt_symmetry, + VIEW3D_PT_curves_sculpt_symmetry_for_topbar, + VIEW3D_PT_tools_weightpaint_symmetry, VIEW3D_PT_tools_weightpaint_symmetry_for_topbar, VIEW3D_PT_tools_weightpaint_options, @@ -2357,7 +2423,8 @@ classes = ( VIEW3D_PT_tools_grease_pencil_paint_appearance, VIEW3D_PT_tools_grease_pencil_sculpt_select, VIEW3D_PT_tools_grease_pencil_sculpt_settings, - VIEW3D_PT_tools_grease_pencil_sculpt_options, + VIEW3D_PT_tools_grease_pencil_sculpt_brush_advanced, + VIEW3D_PT_tools_grease_pencil_sculpt_brush_popover, VIEW3D_PT_tools_grease_pencil_sculpt_appearance, VIEW3D_PT_tools_grease_pencil_weight_paint_select, VIEW3D_PT_tools_grease_pencil_weight_paint_settings, diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index f9bd4342b6e..f5282123ce8 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -419,12 +419,10 @@ shader_node_categories = [ NodeItem("ShaderNodeRGBToBW"), NodeItem("ShaderNodeShaderToRGB", poll=object_eevee_shader_nodes_poll), NodeItem("ShaderNodeVectorMath"), - NodeItem("ShaderNodeSeparateRGB"), - NodeItem("ShaderNodeCombineRGB"), + NodeItem("ShaderNodeSeparateColor"), + NodeItem("ShaderNodeCombineColor"), NodeItem("ShaderNodeSeparateXYZ"), NodeItem("ShaderNodeCombineXYZ"), - NodeItem("ShaderNodeSeparateHSV"), - NodeItem("ShaderNodeCombineHSV"), NodeItem("ShaderNodeWavelength"), NodeItem("ShaderNodeBlackbody"), ]), @@ -483,14 +481,8 @@ compositor_node_categories = [ NodeItem("CompositorNodePremulKey"), NodeItem("CompositorNodeIDMask"), NodeItem("CompositorNodeRGBToBW"), - NodeItem("CompositorNodeSepRGBA"), - NodeItem("CompositorNodeCombRGBA"), - NodeItem("CompositorNodeSepHSVA"), - NodeItem("CompositorNodeCombHSVA"), - NodeItem("CompositorNodeSepYUVA"), - NodeItem("CompositorNodeCombYUVA"), - NodeItem("CompositorNodeSepYCCA"), - NodeItem("CompositorNodeCombYCCA"), + NodeItem("CompositorNodeSeparateColor"), + NodeItem("CompositorNodeCombineColor"), NodeItem("CompositorNodeSeparateXYZ"), NodeItem("CompositorNodeCombineXYZ"), NodeItem("CompositorNodeSwitchView"), @@ -576,8 +568,8 @@ texture_node_categories = [ NodeItem("TextureNodeCurveRGB"), NodeItem("TextureNodeInvert"), NodeItem("TextureNodeHueSaturation"), - NodeItem("TextureNodeCompose"), - NodeItem("TextureNodeDecompose"), + NodeItem("TextureNodeCombineColor"), + NodeItem("TextureNodeSeparateColor"), ]), TextureNodeCategory("TEX_PATTERN", "Pattern", items=[ NodeItem("TextureNodeChecker"), @@ -629,8 +621,8 @@ geometry_node_categories = [ NodeItem("ShaderNodeMixRGB"), NodeItem("ShaderNodeRGBCurve"), NodeItem("ShaderNodeValToRGB"), - NodeItem("ShaderNodeSeparateRGB"), - NodeItem("ShaderNodeCombineRGB"), + NodeItem("FunctionNodeSeparateColor"), + NodeItem("FunctionNodeCombineColor"), ]), GeometryNodeCategory("GEO_CURVE", "Curve", items=curve_node_items), GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[ diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index ffc4d37f622..d0592e9a405 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -15,8 +15,9 @@ if(WITH_CLANG_TIDY AND NOT MSVC) endif() find_package(ClangTidy REQUIRED) - set(CMAKE_C_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE}) - set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE}) + set(CMAKE_C_CLANG_TIDY + ${CLANG_TIDY_EXECUTABLE};--extra-arg=-Wno-error=unknown-warning-option) + set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE};--extra-arg=-Wno-error=unknown-warning-option) endif() add_subdirectory(blender) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index f93cb8b2d64..a170f27d247 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -1374,7 +1374,7 @@ bool blf_font_size(FontBLF *font, float size, unsigned int dpi) font->dpi = dpi; } else { - printf("The current font does not support the size, %f and dpi, %u\n", size, dpi); + printf("The current font does not support the size, %f and DPI, %u\n", size, dpi); return false; } } diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 62bce36dda0..9dfcb1a4ad6 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -123,7 +123,7 @@ typedef struct GlyphCacheBLF { /* font size. */ float size; - /* and dpi. */ + /* and DPI. */ unsigned int dpi; bool bold; @@ -264,7 +264,7 @@ typedef struct FontBLF { /* the width to wrap the text, see BLF_WORD_WRAP */ int wrap_width; - /* font dpi (default 72). */ + /* Font DPI (default 72). */ unsigned int dpi; /* font size. */ @@ -276,7 +276,8 @@ typedef struct FontBLF { /* font options. */ int flags; - /* List of glyph caches (GlyphCacheBLF) for this font for size, dpi, bold, italic. + /** + * List of glyph caches (#GlyphCacheBLF) for this font for size, DPI, bold, italic. * Use blf_glyph_cache_acquire(font) and blf_glyph_cache_release(font) to access cache! */ ListBase cache; diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index fea5f515db8..59f0c86684d 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -46,14 +46,9 @@ * as it is and stick with using BMesh and CDDM. */ -#include "DNA_customdata_types.h" -#include "DNA_defs.h" -#include "DNA_meshdata_types.h" - #include "BLI_compiler_attrs.h" -#include "BKE_bvhutils.h" -#include "BKE_customdata.h" +#include "DNA_customdata_types.h" #ifdef __cplusplus extern "C" { @@ -125,7 +120,6 @@ struct DerivedMesh { /* Also called in Editmode */ int (*getNumVerts)(DerivedMesh *dm); int (*getNumEdges)(DerivedMesh *dm); - int (*getNumTessFaces)(DerivedMesh *dm); int (*getNumLoops)(DerivedMesh *dm); int (*getNumPolys)(DerivedMesh *dm); @@ -233,15 +227,6 @@ bool DM_release(DerivedMesh *dm); */ void DM_set_only_copy(DerivedMesh *dm, const struct CustomData_MeshMasks *mask); -/* Adds a vertex/edge/face custom data layer to a DerivedMesh, optionally - * backed by an external data array - * alloctype defines how the layer is allocated or copied, and how it is - * freed, see BKE_customdata.h for the different options. */ - -void DM_add_vert_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer); -void DM_add_edge_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer); -void DM_add_poly_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer); - /* -------------------------------------------------------------------- */ /** \name Custom Data Layer Access Functions * diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 15fdc73adeb..ab13a2e85d0 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -17,15 +17,15 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 302 +#define BLENDER_VERSION 303 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ -#define BLENDER_VERSION_CYCLE beta +#define BLENDER_VERSION_CYCLE alpha /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 14 +#define BLENDER_FILE_SUBVERSION 0 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_camera.h b/source/blender/blenkernel/BKE_camera.h index 57dc1e288dc..e55b8b1a2da 100644 --- a/source/blender/blenkernel/BKE_camera.h +++ b/source/blender/blenkernel/BKE_camera.h @@ -79,7 +79,7 @@ typedef struct CameraParams { void BKE_camera_params_init(CameraParams *params); void BKE_camera_params_from_object(CameraParams *params, const struct Object *cam_ob); void BKE_camera_params_from_view3d(CameraParams *params, - struct Depsgraph *depsgraph, + const struct Depsgraph *depsgraph, const struct View3D *v3d, const struct RegionView3D *rv3d); diff --git a/source/blender/blenkernel/BKE_colortools.h b/source/blender/blenkernel/BKE_colortools.h index 0d4560207ea..d52fd91ccdd 100644 --- a/source/blender/blenkernel/BKE_colortools.h +++ b/source/blender/blenkernel/BKE_colortools.h @@ -129,6 +129,36 @@ bool BKE_curvemapping_RGBA_does_something(const struct CurveMapping *cumap); void BKE_curvemapping_table_F(const struct CurveMapping *cumap, float **array, int *size); void BKE_curvemapping_table_RGBA(const struct CurveMapping *cumap, float **array, int *size); +/** Get the minimum x value of each curve map table. */ +void BKE_curvemapping_get_range_minimums(const struct CurveMapping *curve_mapping, + float minimums[4]); + +/** Get the reciprocal of the difference between the maximum and the minimum x value of each curve + * map table. Evaluation parameters can be multiplied by this value to be normalized. If the + * difference is zero, 1^8 is returned. */ +void BKE_curvemapping_compute_range_dividers(const struct CurveMapping *curve_mapping, + float dividers[4]); + +/** Compute the slopes at the start and end points of each curve map. The slopes are multiplied by + * the range of the curve map to compensate for parameter normalization. If the slope is vertical, + * 1^8 is returned. */ +void BKE_curvemapping_compute_slopes(const struct CurveMapping *curve_mapping, + float start_slopes[4], + float end_slopes[4]); + +/** Check if the curve map at the index is identity, that is, does nothing. A curve map is said to + * be identity if: + * - The curve mapping uses extrapolation. + * - Its range is 1. + * - The slope at its start point is 1. + * - The slope at its end point is 1. + * - The number of points is 2. + * - The start point is at (0, 0). + * - The end point is at (1, 1). + * Note that this could return false even if the curve map is identity, this happens in the case + * when more than 2 points exist in the curve map but all points are collinear. */ +bool BKE_curvemapping_is_map_identity(const struct CurveMapping *curve_mapping, int index); + /** * Call when you do images etc, needs restore too. also verifies tables. * non-const (these modify the curve). diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h index b3119666f0a..ce1f4c2b98c 100644 --- a/source/blender/blenkernel/BKE_constraint.h +++ b/source/blender/blenkernel/BKE_constraint.h @@ -67,7 +67,7 @@ typedef void (*ConstraintIDFunc)(struct bConstraint *con, * Callers of these functions must check that they actually point to something useful, * as some constraints don't define some of these. * - * Warning: + * WARNING: * it is not too advisable to reorder order of members of this struct, * as you'll have to edit quite a few #NUM_CONSTRAINT_TYPES of these * structs. diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 1e96c0e4c41..87fa26a4f73 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -48,6 +48,13 @@ struct BasisCache { * In other words, the index of the first control point that influences this evaluated point. */ Vector<int> start_indices; + + /** + * The result of #check_valid_num_and_order, to avoid retrieving its inputs later on. + * If this is true, the data above will be invalid, and original data should be copied + * to the evaluated result. + */ + bool invalid = false; }; } // namespace curves::nurbs @@ -120,7 +127,7 @@ class CurvesGeometry : public ::CurvesGeometry { * Create curves with the given size. Only the position attribute is created, along with the * offsets. */ - CurvesGeometry(int point_size, int curve_size); + CurvesGeometry(int point_num, int curve_num); CurvesGeometry(const CurvesGeometry &other); CurvesGeometry(CurvesGeometry &&other); CurvesGeometry &operator=(const CurvesGeometry &other); @@ -174,11 +181,18 @@ class CurvesGeometry : public ::CurvesGeometry { /** Update the cached count of curves of each type, necessary after #curve_types_for_write. */ void update_curve_types(); - bool has_curve_with_type(const CurveType type) const; + bool has_curve_with_type(CurveType type) const; /** Return true if all of the curves have the provided type. */ bool is_single_type(CurveType type) const; /** Return the number of curves with each type. */ const std::array<int, CURVE_TYPES_NUM> &curve_type_counts() const; + /** + * All of the curve indices for curves with a specific type. + */ + IndexMask indices_for_curve_type(CurveType type, Vector<int64_t> &r_indices) const; + IndexMask indices_for_curve_type(CurveType type, + IndexMask selection, + Vector<int64_t> &r_indices) const; Span<float3> positions() const; MutableSpan<float3> positions_for_write(); @@ -276,11 +290,6 @@ class CurvesGeometry : public ::CurvesGeometry { bool bounds_min_max(float3 &min, float3 &max) const; private: - /** - * All of the curve indices for curves with a specific type. - */ - IndexMask indices_for_curve_type(CurveType type, Vector<int64_t> &r_indices) const; - /* -------------------------------------------------------------------- * Evaluation. */ @@ -409,7 +418,7 @@ namespace curves { * The number of segments between control points, accounting for the last segment of cyclic * curves. The logic is simple, but this function should be used to make intentions clearer. */ -inline int curve_segment_size(const int points_num, const bool cyclic) +inline int curve_segment_num(const int points_num, const bool cyclic) { BLI_assert(points_num > 0); return (cyclic && points_num > 1) ? points_num : points_num - 1; @@ -576,11 +585,11 @@ namespace catmull_rom { * \param points_num: The number of points in the curve. * \param resolution: The resolution for each segment. */ -int calculate_evaluated_size(int points_num, bool cyclic, int resolution); +int calculate_evaluated_num(int points_num, bool cyclic, int resolution); /** * Evaluate the Catmull Rom curve. The length of the #dst span should be calculated with - * #calculate_evaluated_size and is expected to divide evenly by the #src span's segment size. + * #calculate_evaluated_num and is expected to divide evenly by the #src span's segment size. */ void interpolate_to_evaluated(GSpan src, bool cyclic, int resolution, GMutableSpan dst); @@ -597,7 +606,7 @@ namespace nurbs { /** * Checks the conditions that a NURBS curve needs to evaluate. */ -bool check_valid_size_and_order(int points_num, int8_t order, bool cyclic, KnotsMode knots_mode); +bool check_valid_num_and_order(int points_num, int8_t order, bool cyclic, KnotsMode knots_mode); /** * Calculate the standard evaluated size for a NURBS curve, using the standard that @@ -607,7 +616,7 @@ bool check_valid_size_and_order(int points_num, int8_t order, bool cyclic, Knots * for predictability and so that cached basis weights of NURBS curves with these properties can be * shared. */ -int calculate_evaluated_size( +int calculate_evaluated_num( int points_num, int8_t order, bool cyclic, int resolution, KnotsMode knots_mode); /** @@ -615,7 +624,7 @@ int calculate_evaluated_size( * The knots must be longer for a cyclic curve, for example, in order to provide weights for the * last evaluated points that are also influenced by the first control points. */ -int knots_size(int points_num, int8_t order, bool cyclic); +int knots_num(int points_num, int8_t order, bool cyclic); /** * Calculate the knots for a curve given its properties, based on built-in standards defined by @@ -635,7 +644,7 @@ void calculate_knots( * and a weight for each control point. */ void calculate_basis_cache(int points_num, - int evaluated_size, + int evaluated_num, int8_t order, bool cyclic, Span<float> knots, @@ -662,6 +671,7 @@ void interpolate_to_evaluated(const BasisCache &basis_cache, } // namespace curves Curves *curves_new_nomain(int points_num, int curves_num); +Curves *curves_new_nomain(CurvesGeometry curves); /** * Create a new curves data-block containing a single curve with the given length and type. @@ -676,11 +686,11 @@ std::array<int, CURVE_TYPES_NUM> calculate_type_counts(const VArray<int8_t> &typ inline int CurvesGeometry::points_num() const { - return this->point_size; + return this->point_num; } inline int CurvesGeometry::curves_num() const { - return this->curve_size; + return this->curve_num; } inline IndexRange CurvesGeometry::points_range() const { @@ -710,7 +720,7 @@ inline const std::array<int, CURVE_TYPES_NUM> &CurvesGeometry::curve_type_counts inline IndexRange CurvesGeometry::points_for_curve(const int index) const { /* Offsets are not allocated when there are no curves. */ - BLI_assert(this->curve_size > 0); + BLI_assert(this->curve_num > 0); BLI_assert(this->curve_offsets != nullptr); const int offset = this->curve_offsets[index]; const int offset_next = this->curve_offsets[index + 1]; @@ -720,7 +730,7 @@ inline IndexRange CurvesGeometry::points_for_curve(const int index) const inline IndexRange CurvesGeometry::points_for_curves(const IndexRange curves) const { /* Offsets are not allocated when there are no curves. */ - BLI_assert(this->curve_size > 0); + BLI_assert(this->curve_num > 0); BLI_assert(this->curve_offsets != nullptr); const int offset = this->curve_offsets[curves.start()]; const int offset_next = this->curve_offsets[curves.one_after_last()]; @@ -742,7 +752,7 @@ inline IndexRange CurvesGeometry::evaluated_points_for_curve(int index) const inline IndexRange CurvesGeometry::evaluated_points_for_curves(const IndexRange curves) const { BLI_assert(!this->runtime->offsets_cache_dirty); - BLI_assert(this->curve_size > 0); + BLI_assert(this->curve_num > 0); const int offset = this->runtime->evaluated_offsets_cache[curves.start()]; const int offset_next = this->runtime->evaluated_offsets_cache[curves.one_after_last()]; return {offset, offset_next - offset}; @@ -760,7 +770,7 @@ inline IndexRange CurvesGeometry::lengths_range_for_curve(const int curve_index, BLI_assert(cyclic == this->cyclic()[curve_index]); const IndexRange points = this->evaluated_points_for_curve(curve_index); const int start = points.start() + curve_index; - return {start, points.is_empty() ? 0 : curves::curve_segment_size(points.size(), cyclic)}; + return {start, curves::curve_segment_num(points.size(), cyclic)}; } inline Span<float> CurvesGeometry::evaluated_lengths_for_curve(const int curve_index, @@ -775,8 +785,7 @@ inline float CurvesGeometry::evaluated_length_total_for_curve(const int curve_in const bool cyclic) const { const Span<float> lengths = this->evaluated_lengths_for_curve(curve_index, cyclic); - /* Check for curves that have no evaluated segments. */ - return lengths.is_empty() ? 0.0f : lengths.last(); + return lengths.last(); } /** \} */ diff --git a/source/blender/blenkernel/BKE_curves_utils.hh b/source/blender/blenkernel/BKE_curves_utils.hh new file mode 100644 index 00000000000..62b060093e9 --- /dev/null +++ b/source/blender/blenkernel/BKE_curves_utils.hh @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BKE_curves.hh" + +/** \file + * \ingroup bke + * \brief Low-level operations for curves. + */ + +namespace blender::bke::curves { + +/** + * Copy the size of every curve in #curve_ranges to the corresponding index in #counts. + */ +void fill_curve_counts(const bke::CurvesGeometry &curves, + Span<IndexRange> curve_ranges, + MutableSpan<int> counts); + +/** + * Turn an array of sizes into the offset at each index including all previous sizes. + */ +void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, int start_offset = 0); + +} // namespace blender::bke::curves diff --git a/source/blender/blenkernel/BKE_customdata_file.h b/source/blender/blenkernel/BKE_customdata_file.h index 9d45d28bd18..6972dcb8681 100644 --- a/source/blender/blenkernel/BKE_customdata_file.h +++ b/source/blender/blenkernel/BKE_customdata_file.h @@ -25,17 +25,17 @@ void cdf_free(CDataFile *cdf); /* File read/write/remove */ -bool cdf_read_open(CDataFile *cdf, const char *filename); +bool cdf_read_open(CDataFile *cdf, const char *filepath); bool cdf_read_layer(CDataFile *cdf, CDataFileLayer *blay); bool cdf_read_data(CDataFile *cdf, unsigned int size, void *data); void cdf_read_close(CDataFile *cdf); -bool cdf_write_open(CDataFile *cdf, const char *filename); +bool cdf_write_open(CDataFile *cdf, const char *filepath); bool cdf_write_layer(CDataFile *cdf, CDataFileLayer *blay); bool cdf_write_data(CDataFile *cdf, unsigned int size, void *data); void cdf_write_close(CDataFile *cdf); -void cdf_remove(const char *filename); +void cdf_remove(const char *filepath); /* Layers */ diff --git a/source/blender/blenkernel/BKE_deform.h b/source/blender/blenkernel/BKE_deform.h index 1b225884b14..d32f754c6c0 100644 --- a/source/blender/blenkernel/BKE_deform.h +++ b/source/blender/blenkernel/BKE_deform.h @@ -228,35 +228,38 @@ void BKE_defvert_normalize_lock_map(struct MDeformVert *dvert, /* Utilities to 'extract' a given vgroup into a simple float array, * for verts, but also edges/polys/loops. */ -void BKE_defvert_extract_vgroup_to_vertweights( - struct MDeformVert *dvert, int defgroup, int num_verts, float *r_weights, bool invert_vgroup); +void BKE_defvert_extract_vgroup_to_vertweights(const struct MDeformVert *dvert, + int defgroup, + int num_verts, + bool invert_vgroup, + float *r_weights); /** * The following three make basic interpolation, * using temp vert_weights array to avoid looking up same weight several times. */ -void BKE_defvert_extract_vgroup_to_edgeweights(struct MDeformVert *dvert, +void BKE_defvert_extract_vgroup_to_edgeweights(const struct MDeformVert *dvert, int defgroup, int num_verts, struct MEdge *edges, int num_edges, - float *r_weights, - bool invert_vgroup); -void BKE_defvert_extract_vgroup_to_loopweights(struct MDeformVert *dvert, + bool invert_vgroup, + float *r_weights); +void BKE_defvert_extract_vgroup_to_loopweights(const struct MDeformVert *dvert, int defgroup, int num_verts, struct MLoop *loops, int num_loops, - float *r_weights, - bool invert_vgroup); -void BKE_defvert_extract_vgroup_to_polyweights(struct MDeformVert *dvert, + bool invert_vgroup, + float *r_weights); +void BKE_defvert_extract_vgroup_to_polyweights(const struct MDeformVert *dvert, int defgroup, int num_verts, struct MLoop *loops, int num_loops, struct MPoly *polys, int num_polys, - float *r_weights, - bool invert_vgroup); + bool invert_vgroup, + float *r_weights); void BKE_defvert_weight_to_rgb(float r_rgb[3], float weight); diff --git a/source/blender/blenkernel/BKE_geometry_fields.hh b/source/blender/blenkernel/BKE_geometry_fields.hh index 36b382feb5f..9c86ab262ef 100644 --- a/source/blender/blenkernel/BKE_geometry_fields.hh +++ b/source/blender/blenkernel/BKE_geometry_fields.hh @@ -162,4 +162,14 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput { bool is_equal_to(const fn::FieldNode &other) const override; }; +class CurveLengthFieldInput final : public GeometryFieldInput { + public: + CurveLengthFieldInput(); + GVArray get_varray_for_context(const GeometryComponent &component, + AttributeDomain domain, + IndexMask mask) const final; + uint64_t hash() const override; + bool is_equal_to(const fn::FieldNode &other) const override; +}; + } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index dfd9fccebbd..849c430fd7b 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -98,7 +98,7 @@ class GeometryComponent { /** * Return the length of a specific domain, or 0 if the domain is not supported. */ - virtual int attribute_domain_size(AttributeDomain domain) const; + virtual int attribute_domain_num(AttributeDomain domain) const; /** * Return true if the attribute name corresponds to a built-in attribute with a hardcoded domain @@ -560,7 +560,7 @@ class MeshComponent : public GeometryComponent { */ Mesh *get_for_write(); - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; bool is_empty() const final; @@ -623,7 +623,7 @@ class PointCloudComponent : public GeometryComponent { */ PointCloud *get_for_write(); - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; bool is_empty() const final; @@ -664,7 +664,7 @@ class CurveComponentLegacy : public GeometryComponent { const CurveEval *get_for_read() const; CurveEval *get_for_write(); - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; bool is_empty() const final; @@ -715,7 +715,7 @@ class CurveComponent : public GeometryComponent { const Curves *get_for_read() const; Curves *get_for_write(); - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; bool is_empty() const final; @@ -949,8 +949,8 @@ class InstancesComponent : public GeometryComponent { blender::MutableSpan<blender::float4x4> instance_transforms(); blender::Span<blender::float4x4> instance_transforms() const; - int instances_amount() const; - int references_amount() const; + int instances_num() const; + int references_num() const; /** * Remove the indices that are not contained in the mask input, and remove unused instance @@ -963,7 +963,7 @@ class InstancesComponent : public GeometryComponent { blender::bke::CustomDataAttributes &attributes(); const blender::bke::CustomDataAttributes &attributes() const; - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; void foreach_referenced_geometry( blender::FunctionRef<void(const GeometrySet &geometry_set)> callback) const; diff --git a/source/blender/blenkernel/BKE_icons.h b/source/blender/blenkernel/BKE_icons.h index db45e405e79..8d9351806c4 100644 --- a/source/blender/blenkernel/BKE_icons.h +++ b/source/blender/blenkernel/BKE_icons.h @@ -172,7 +172,7 @@ bool BKE_previewimg_id_supports_jobs(const struct ID *id); /** * Trigger deferred loading of a custom image file into the preview buffer. */ -void BKE_previewimg_id_custom_set(struct ID *id, const char *path); +void BKE_previewimg_id_custom_set(struct ID *id, const char *filepath); /** * Free the preview image belonging to the id. @@ -223,11 +223,12 @@ struct PreviewImage *BKE_previewimg_cached_get(const char *name); struct PreviewImage *BKE_previewimg_cached_ensure(const char *name); /** - * Generate a #PreviewImage from given file path, using thumbnails management, if not yet existing. - * Does not actually generate the preview, #BKE_previewimg_ensure() must be called for that. + * Generate a #PreviewImage from given `filepath`, using thumbnails management, if not yet + * existing. Does not actually generate the preview, #BKE_previewimg_ensure() must be called for + * that. */ struct PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name, - const char *path, + const char *filepath, int source, bool force_update); diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 42d0e66cf49..b6607a92c14 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -273,14 +273,6 @@ bool BKE_image_is_openexr(struct Image *ima); void BKE_image_backup_render(struct Scene *scene, struct Image *ima, bool free_current_slot); /** - * For single-layer OpenEXR saving. - */ -bool BKE_image_save_openexr_multiview(struct Image *ima, - struct ImBuf *ibuf, - const char *filepath, - int flags); - -/** * Goes over all textures that use images. */ void BKE_image_free_all_textures(struct Main *bmain); @@ -387,7 +379,7 @@ void BKE_image_ensure_tile_token(char *filename); /** * When provided with an absolute virtual `filepath`, check to see if at least * one concrete file exists. - * Note: This function requires directory traversal and may be inefficient in time-critical, + * NOTE: This function requires directory traversal and may be inefficient in time-critical, * or iterative, code paths. */ bool BKE_image_tile_filepath_exists(const char *filepath); @@ -454,7 +446,7 @@ bool BKE_image_is_dirty_writable(struct Image *image, bool *r_is_writable); int BKE_image_sequence_guess_offset(struct Image *image); bool BKE_image_has_anim(struct Image *image); bool BKE_image_has_packedfile(const struct Image *image); -bool BKE_image_has_filepath(struct Image *ima); +bool BKE_image_has_filepath(const struct Image *ima); /** * Checks the image buffer changes with time (not keyframed values). */ diff --git a/source/blender/blenkernel/BKE_image_format.h b/source/blender/blenkernel/BKE_image_format.h index 633af54ea4f..6a03d1d8df5 100644 --- a/source/blender/blenkernel/BKE_image_format.h +++ b/source/blender/blenkernel/BKE_image_format.h @@ -76,6 +76,8 @@ char BKE_imtype_from_arg(const char *arg); void BKE_image_format_from_imbuf(struct ImageFormatData *im_format, const struct ImBuf *imbuf); void BKE_image_format_to_imbuf(struct ImBuf *ibuf, const struct ImageFormatData *imf); +bool BKE_image_format_is_byte(const struct ImageFormatData *imf); + /* Color Management */ void BKE_image_format_color_management_copy(struct ImageFormatData *imf, diff --git a/source/blender/blenkernel/BKE_image_save.h b/source/blender/blenkernel/BKE_image_save.h index 052fc937af9..b5db15bb174 100644 --- a/source/blender/blenkernel/BKE_image_save.h +++ b/source/blender/blenkernel/BKE_image_save.h @@ -13,10 +13,11 @@ extern "C" { #endif struct Image; +struct ImageUser; struct Main; +struct RenderResult; struct ReportList; struct Scene; -struct RenderResult; /* Image datablock saving. */ @@ -34,11 +35,19 @@ typedef struct ImageSaveOptions { bool save_copy; bool save_as_render; bool do_newpath; + + /* Keep track of previous values for auto updates in UI. */ + bool prev_save_as_render; + int prev_imtype; } ImageSaveOptions; -void BKE_image_save_options_init(struct ImageSaveOptions *opts, +bool BKE_image_save_options_init(ImageSaveOptions *opts, struct Main *bmain, - struct Scene *scene); + struct Scene *scene, + struct Image *ima, + struct ImageUser *iuser, + const bool guess_path); +void BKE_image_save_options_update(struct ImageSaveOptions *opts, struct Image *ima); void BKE_image_save_options_free(struct ImageSaveOptions *opts); bool BKE_image_save(struct ReportList *reports, diff --git a/source/blender/blenkernel/BKE_lib_principle_properties.h b/source/blender/blenkernel/BKE_lib_principle_properties.h index 10d0d33a0c4..42177204efb 100644 --- a/source/blender/blenkernel/BKE_lib_principle_properties.h +++ b/source/blender/blenkernel/BKE_lib_principle_properties.h @@ -40,7 +40,7 @@ struct ReportList; struct IDPrincipleProperties *BKE_lib_principleprop_init(struct ID *id); #if 0 /** - * Shallow or deep copy of a whole princple properties from \a src_id to \a dst_id. + * Shallow or deep copy of a whole principle properties from \a src_id to \a dst_id. */ void BKE_lib_principleprop_copy(struct ID *dst_id, const struct ID *src_id, bool do_full_copy); #endif diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h index f38f6afff7e..05e502f06c2 100644 --- a/source/blender/blenkernel/BKE_material.h +++ b/source/blender/blenkernel/BKE_material.h @@ -52,7 +52,7 @@ void BKE_object_material_remap_calc(struct Object *ob_dst, */ void BKE_object_material_from_eval_data(struct Main *bmain, struct Object *ob_orig, - struct ID *data_eval); + const struct ID *data_eval); struct Material *BKE_material_add(struct Main *bmain, const char *name); struct Material *BKE_gpencil_material_add(struct Main *bmain, const char *name); void BKE_gpencil_material_attr_init(struct Material *ma); diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 091f30825ae..23ec69babc8 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -204,6 +204,7 @@ bool BKE_mesh_material_index_used(struct Mesh *me, short index); void BKE_mesh_material_index_clear(struct Mesh *me); void BKE_mesh_material_remap(struct Mesh *me, const unsigned int *remap, unsigned int remap_len); void BKE_mesh_smooth_flag_set(struct Mesh *me, bool use_smooth); +void BKE_mesh_auto_smooth_flag_set(struct Mesh *me, bool use_auto_smooth, float auto_smooth_angle); /** * Needed after converting a mesh with subsurf optimal display to mesh. @@ -931,13 +932,6 @@ void BKE_mesh_flush_select_from_polys_ex(struct MVert *mvert, const struct MPoly *mpoly, int totpoly); void BKE_mesh_flush_select_from_polys(struct Mesh *me); -void BKE_mesh_flush_select_from_verts_ex(const struct MVert *mvert, - int totvert, - const struct MLoop *mloop, - struct MEdge *medge, - int totedge, - struct MPoly *mpoly, - int totpoly); void BKE_mesh_flush_select_from_verts(struct Mesh *me); /* spatial evaluation */ diff --git a/source/blender/blenkernel/BKE_mesh_tangent.h b/source/blender/blenkernel/BKE_mesh_tangent.h index 27061a5766e..58142653a90 100644 --- a/source/blender/blenkernel/BKE_mesh_tangent.h +++ b/source/blender/blenkernel/BKE_mesh_tangent.h @@ -21,7 +21,7 @@ void BKE_mesh_calc_loop_tangent_single_ex(const struct MVert *mverts, int numVerts, const struct MLoop *mloops, float (*r_looptangent)[4], - float (*loopnors)[3], + const float (*loopnors)[3], const struct MLoopUV *loopuv, int numLoops, const struct MPoly *mpolys, diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 881e86ccc54..866b0353d07 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -102,6 +102,7 @@ typedef enum { /** Accepts #BMesh input (without conversion). */ eModifierTypeFlag_AcceptsBMesh = (1 << 11), } ModifierTypeFlag; +ENUM_OPERATORS(ModifierTypeFlag, eModifierTypeFlag_AcceptsBMesh) typedef void (*IDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cb_flag); typedef void (*TexWalkFunc)(void *userData, @@ -113,7 +114,7 @@ typedef enum ModifierApplyFlag { /** Render time. */ MOD_APPLY_RENDER = 1 << 0, /** Result of evaluation will be cached, so modifier might - * want to cache data for quick updates (used by subsurf) */ + * want to cache data for quick updates (used by subdivision-surface) */ MOD_APPLY_USECACHE = 1 << 1, /** Modifier evaluated for undeformed texture coordinates */ MOD_APPLY_ORCO = 1 << 2, @@ -363,7 +364,9 @@ typedef struct ModifierTypeInfo { * This method should write any additional arrays and referenced structs that should be * stored in the file. */ - void (*blendWrite)(struct BlendWriter *writer, const struct ModifierData *md); + void (*blendWrite)(struct BlendWriter *writer, + const struct ID *id_owner, + const struct ModifierData *md); /** * Is called when the modifier is read from a file. @@ -592,7 +595,9 @@ struct Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(struct Object void BKE_modifier_check_uuids_unique_and_report(const struct Object *object); -void BKE_modifier_blend_write(struct BlendWriter *writer, struct ListBase *modbase); +void BKE_modifier_blend_write(struct BlendWriter *writer, + const struct ID *id_owner, + struct ListBase *modbase); void BKE_modifier_blend_read_data(struct BlendDataReader *reader, struct ListBase *lb, struct Object *ob); diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h index 0efe38c1e8f..dfa330ff508 100644 --- a/source/blender/blenkernel/BKE_multires.h +++ b/source/blender/blenkernel/BKE_multires.h @@ -130,7 +130,7 @@ void multiresModifier_prepare_join(struct Depsgraph *depsgraph, struct Object *ob, struct Object *to_ob); -int multires_mdisp_corners(struct MDisps *s); +int multires_mdisp_corners(const struct MDisps *s); /** * Update multi-res data after topology changing. diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index c9228db9ecc..1ff10d06b00 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1090,8 +1090,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define SH_NODE_SQUEEZE 117 //#define SH_NODE_MATERIAL_EXT 118 #define SH_NODE_INVERT 119 -#define SH_NODE_SEPRGB 120 -#define SH_NODE_COMBRGB 121 +#define SH_NODE_SEPRGB_LEGACY 120 +#define SH_NODE_COMBRGB_LEGACY 121 #define SH_NODE_HUE_SAT 122 #define SH_NODE_OUTPUT_MATERIAL 124 @@ -1147,8 +1147,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define SH_NODE_WAVELENGTH 180 #define SH_NODE_BLACKBODY 181 #define SH_NODE_VECT_TRANSFORM 182 -#define SH_NODE_SEPHSV 183 -#define SH_NODE_COMBHSV 184 +#define SH_NODE_SEPHSV_LEGACY 183 +#define SH_NODE_COMBHSV_LEGACY 184 #define SH_NODE_BSDF_HAIR 185 // #define SH_NODE_LAMP 186 #define SH_NODE_UVMAP 187 @@ -1175,6 +1175,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define SH_NODE_VECTOR_ROTATE 708 #define SH_NODE_CURVE_FLOAT 709 #define SH_NODE_POINT_INFO 710 +#define SH_NODE_COMBINE_COLOR 711 +#define SH_NODE_SEPARATE_COLOR 712 /** \} */ @@ -1202,8 +1204,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define CMP_NODE_MAP_VALUE 213 #define CMP_NODE_TIME 214 #define CMP_NODE_VECBLUR 215 -#define CMP_NODE_SEPRGBA 216 -#define CMP_NODE_SEPHSVA 217 +#define CMP_NODE_SEPRGBA_LEGACY 216 +#define CMP_NODE_SEPHSVA_LEGACY 217 #define CMP_NODE_SETALPHA 218 #define CMP_NODE_HUE_SAT 219 #define CMP_NODE_IMAGE 220 @@ -1213,14 +1215,14 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define CMP_NODE_TEXTURE 224 #define CMP_NODE_TRANSLATE 225 #define CMP_NODE_ZCOMBINE 226 -#define CMP_NODE_COMBRGBA 227 +#define CMP_NODE_COMBRGBA_LEGACY 227 #define CMP_NODE_DILATEERODE 228 #define CMP_NODE_ROTATE 229 #define CMP_NODE_SCALE 230 -#define CMP_NODE_SEPYCCA 231 -#define CMP_NODE_COMBYCCA 232 -#define CMP_NODE_SEPYUVA 233 -#define CMP_NODE_COMBYUVA 234 +#define CMP_NODE_SEPYCCA_LEGACY 231 +#define CMP_NODE_COMBYCCA_LEGACY 232 +#define CMP_NODE_SEPYUVA_LEGACY 233 +#define CMP_NODE_COMBYUVA_LEGACY 234 #define CMP_NODE_DIFF_MATTE 235 #define CMP_NODE_COLOR_SPILL 236 #define CMP_NODE_CHROMA_MATTE 237 @@ -1232,7 +1234,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define CMP_NODE_ID_MASK 243 #define CMP_NODE_DEFOCUS 244 #define CMP_NODE_DISPLACE 245 -#define CMP_NODE_COMBHSVA 246 +#define CMP_NODE_COMBHSVA_LEGACY 246 #define CMP_NODE_MATH 247 #define CMP_NODE_LUMA_MATTE 248 #define CMP_NODE_BRIGHTCONTRAST 249 @@ -1289,6 +1291,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define CMP_NODE_SCENE_TIME 329 #define CMP_NODE_SEPARATE_XYZ 330 #define CMP_NODE_COMBINE_XYZ 331 +#define CMP_NODE_COMBINE_COLOR 332 +#define CMP_NODE_SEPARATE_COLOR 333 /* channel toggles */ #define CMP_CHAN_RGB 1 @@ -1354,11 +1358,13 @@ struct TexResult; #define TEX_NODE_TRANSLATE 416 #define TEX_NODE_COORD 417 #define TEX_NODE_DISTANCE 418 -#define TEX_NODE_COMPOSE 419 -#define TEX_NODE_DECOMPOSE 420 +#define TEX_NODE_COMPOSE_LEGACY 419 +#define TEX_NODE_DECOMPOSE_LEGACY 420 #define TEX_NODE_VALTONOR 421 #define TEX_NODE_SCALE 422 #define TEX_NODE_AT 423 +#define TEX_NODE_COMBINE_COLOR 424 +#define TEX_NODE_SEPARATE_COLOR 425 /* 501-599 reserved. Use like this: TEX_NODE_PROC + TEX_CLOUDS, etc */ #define TEX_NODE_PROC 500 @@ -1511,6 +1517,8 @@ struct TexResult; #define FN_NODE_REPLACE_STRING 1218 #define FN_NODE_INPUT_BOOL 1219 #define FN_NODE_INPUT_INT 1220 +#define FN_NODE_SEPARATE_COLOR 1221 +#define FN_NODE_COMBINE_COLOR 1222 /** \} */ diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 0e976f04dd1..c39ab22ce3a 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -180,6 +180,7 @@ struct Paint *BKE_paint_get_active_from_context(const struct bContext *C); ePaintMode BKE_paintmode_get_active_from_context(const struct bContext *C); ePaintMode BKE_paintmode_get_from_tool(const struct bToolRef *tref); struct Brush *BKE_paint_brush(struct Paint *paint); +const struct Brush *BKE_paint_brush_for_read(const struct Paint *p); void BKE_paint_brush_set(struct Paint *paint, struct Brush *br); struct Palette *BKE_paint_palette(struct Paint *paint); void BKE_paint_palette_set(struct Paint *p, struct Palette *palette); @@ -733,7 +734,7 @@ enum { /* paint_vertex.cc */ /** - * Fills the object's active color atribute layer with the fill color. + * Fills the object's active color attribute layer with the fill color. * * \param[in] ob: The object. * \param[in] fill_color: The fill color. diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index 123fdbb8bac..5f8b2fafdd3 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -583,7 +583,7 @@ void psys_interpolate_face(struct Mesh *mesh, const float (*vert_normals)[3], struct MFace *mface, struct MTFace *tface, - float (*orcodata)[3], + const float (*orcodata)[3], float w[4], float vec[3], float nor[3], diff --git a/source/blender/blenkernel/BKE_pbvh_pixels.hh b/source/blender/blenkernel/BKE_pbvh_pixels.hh index fdfb5fa037e..e73950e6299 100644 --- a/source/blender/blenkernel/BKE_pbvh_pixels.hh +++ b/source/blender/blenkernel/BKE_pbvh_pixels.hh @@ -132,12 +132,22 @@ struct UDIMTilePixels { } }; +struct UDIMTileUndo { + short tile_number; + rcti region; + + UDIMTileUndo(short tile_number, rcti ®ion) : tile_number(tile_number), region(region) + { + } +}; + struct NodeData { struct { bool dirty : 1; } flags; Vector<UDIMTilePixels> tiles; + Vector<UDIMTileUndo> undo_regions; Triangles triangles; NodeData() @@ -155,6 +165,23 @@ struct NodeData { return nullptr; } + void rebuild_undo_regions() + { + undo_regions.clear(); + for (UDIMTilePixels &tile : tiles) { + rcti region; + BLI_rcti_init_minmax(®ion); + for (PackedPixelRow &pixel_row : tile.pixel_rows) { + BLI_rcti_do_minmax_v( + ®ion, int2(pixel_row.start_image_coordinate.x, pixel_row.start_image_coordinate.y)); + BLI_rcti_do_minmax_v(®ion, + int2(pixel_row.start_image_coordinate.x + pixel_row.num_pixels + 1, + pixel_row.start_image_coordinate.y + 1)); + } + undo_regions.append(UDIMTileUndo(tile.tile_number, region)); + } + } + void mark_region(Image &image, const image::ImageTileWrapper &image_tile, ImBuf &image_buffer) { UDIMTilePixels *tile = find_tile_data(image_tile); diff --git a/source/blender/blenkernel/BKE_shrinkwrap.h b/source/blender/blenkernel/BKE_shrinkwrap.h index 2144419728e..3e06bd84805 100644 --- a/source/blender/blenkernel/BKE_shrinkwrap.h +++ b/source/blender/blenkernel/BKE_shrinkwrap.h @@ -73,7 +73,7 @@ typedef struct ShrinkwrapTreeData { BVHTreeFromMesh treeData; const float (*pnors)[3]; - float (*clnors)[3]; + const float (*clnors)[3]; ShrinkwrapBoundaryData *boundary; } ShrinkwrapTreeData; diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 6cbb47dc709..28f326a4ad4 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -102,7 +102,7 @@ class Spline { /** Return the number of control points. */ virtual int size() const = 0; - int segments_size() const; + int segments_num() const; bool is_cyclic() const; void set_cyclic(bool value); @@ -127,8 +127,8 @@ class Spline { * change the generated positions, tangents, normals, mapping, etc. of the evaluated points. */ virtual void mark_cache_invalid() = 0; - virtual int evaluated_points_size() const = 0; - int evaluated_edges_size() const; + virtual int evaluated_points_num() const = 0; + int evaluated_edges_num() const; float length() const; @@ -164,7 +164,7 @@ class Spline { /** * The index of the evaluated point after the result location, accounting for wrapping when * the spline is cyclic. If the sampled factor/length is the very end of the spline, this will - * be the last index (#evaluated_points_size - 1). + * be the last index (#evaluated_points_num - 1). */ int next_evaluated_index; /** @@ -191,7 +191,7 @@ class Spline { * indices and factors to the next index encoded in floats. The logic for converting from the * float values to interpolation data is in #lookup_data_from_index_factor. */ - blender::Array<float> sample_uniform_index_factors(int samples_size) const; + blender::Array<float> sample_uniform_index_factors(int samples_num) const; LookupResult lookup_data_from_index_factor(float index_factor) const; /** @@ -344,7 +344,7 @@ class BezierSpline final : public Spline { bool point_is_sharp(int index) const; void mark_cache_invalid() final; - int evaluated_points_size() const final; + int evaluated_points_num() const final; /** * Returns access to a cache of offsets into the evaluated point array for each control point. @@ -472,7 +472,7 @@ class NURBSpline final : public Spline { /** * Determines where and how the control points affect the evaluated points. The length should - * always be the value returned by #knots_size(), and each value should be greater than or equal + * always be the value returned by #knots_num(), and each value should be greater than or equal * to the previous. Only invalidated when a point is added or removed. */ mutable blender::Vector<float> knots_; @@ -514,8 +514,8 @@ class NURBSpline final : public Spline { uint8_t order() const; void set_order(uint8_t value); - bool check_valid_size_and_order() const; - int knots_size() const; + bool check_valid_num_and_order() const; + int knots_num() const; void resize(int size) final; blender::MutableSpan<blender::float3> positions() final; @@ -530,7 +530,7 @@ class NURBSpline final : public Spline { blender::Span<float> weights() const; void mark_cache_invalid() final; - int evaluated_points_size() const final; + int evaluated_points_num() const final; blender::Span<blender::float3> evaluated_positions() const final; @@ -582,7 +582,7 @@ class PolySpline final : public Spline { blender::Span<float> tilts() const final; void mark_cache_invalid() final; - int evaluated_points_size() const final; + int evaluated_points_num() const final; blender::Span<blender::float3> evaluated_positions() const final; @@ -665,7 +665,7 @@ struct CurveEval { blender::Array<float> accumulated_spline_lengths() const; float total_length() const; - int total_control_point_size() const; + int total_control_point_num() const; void mark_cache_invalid(); diff --git a/source/blender/blenkernel/BKE_subdiv.h b/source/blender/blenkernel/BKE_subdiv.h index 436853fe47b..486c9430279 100644 --- a/source/blender/blenkernel/BKE_subdiv.h +++ b/source/blender/blenkernel/BKE_subdiv.h @@ -186,13 +186,17 @@ typedef struct Subdiv { } cache_; } Subdiv; -/* =================----====--===== MODULE ==========================------== */ +/* -------------------------------------------------------------------- + * Module. + */ /* (De)initialize the entire subdivision surface module. */ void BKE_subdiv_init(void); void BKE_subdiv_exit(void); -/* ========================== CONVERSION HELPERS ============================ */ +/* -------------------------------------------------------------------- + * Conversion helpers. + */ /* NOTE: uv_smooth is eSubsurfUVSmooth. */ eSubdivFVarLinearInterpolation BKE_subdiv_fvar_interpolation_from_uv_smooth(int uv_smooth); @@ -200,7 +204,9 @@ eSubdivFVarLinearInterpolation BKE_subdiv_fvar_interpolation_from_uv_smooth(int eSubdivVtxBoundaryInterpolation BKE_subdiv_vtx_boundary_interpolation_from_subsurf( int boundary_smooth); -/* =============================== STATISTICS =============================== */ +/* -------------------------------------------------------------------- + * Statistics. + */ void BKE_subdiv_stats_init(SubdivStats *stats); @@ -211,11 +217,15 @@ void BKE_subdiv_stats_reset(SubdivStats *stats, eSubdivStatsValue value); void BKE_subdiv_stats_print(const SubdivStats *stats); -/* ================================ SETTINGS ================================ */ +/* -------------------------------------------------------------------- + * Settings. + */ bool BKE_subdiv_settings_equal(const SubdivSettings *settings_a, const SubdivSettings *settings_b); -/* ============================== CONSTRUCTION ============================== */ +/* -------------------------------------------------------------------- + * Construction. + */ /* Construct new subdivision surface descriptor, from scratch, using given * settings and topology. */ @@ -240,7 +250,9 @@ Subdiv *BKE_subdiv_update_from_mesh(Subdiv *subdiv, void BKE_subdiv_free(Subdiv *subdiv); -/* ============================ DISPLACEMENT API ============================ */ +/* -------------------------------------------------------------------- + * Displacement API. + */ void BKE_subdiv_displacement_attach_from_multires(Subdiv *subdiv, struct Mesh *mesh, @@ -248,14 +260,18 @@ void BKE_subdiv_displacement_attach_from_multires(Subdiv *subdiv, void BKE_subdiv_displacement_detach(Subdiv *subdiv); -/* ============================ TOPOLOGY HELPERS ============================ */ +/* -------------------------------------------------------------------- + * Topology helpers. + */ /* For each element in the array, this stores the total number of ptex faces up to that element, * with the total number of ptex faces being the last element in the array. The array is of length * `base face count + 1`. */ int *BKE_subdiv_face_ptex_offset_get(Subdiv *subdiv); -/* =========================== PTEX FACES AND GRIDS ========================= */ +/* -------------------------------------------------------------------- + * PTex faces and grids. + */ /* For a given (ptex_u, ptex_v) within a ptex face get corresponding * (grid_u, grid_v) within a grid. */ diff --git a/source/blender/blenkernel/BKE_subdiv_ccg.h b/source/blender/blenkernel/BKE_subdiv_ccg.h index 31a1912bc68..b30b707759c 100644 --- a/source/blender/blenkernel/BKE_subdiv_ccg.h +++ b/source/blender/blenkernel/BKE_subdiv_ccg.h @@ -8,7 +8,6 @@ #pragma once #include "BKE_DerivedMesh.h" -#include "BKE_customdata.h" #include "BLI_bitmap.h" #include "BLI_sys_types.h" @@ -21,6 +20,8 @@ struct CCGFace; struct CCGKey; struct DMFlagMat; struct Mesh; +struct MPoly; +struct MLoop; struct Subdiv; /* -------------------------------------------------------------------- @@ -309,8 +310,8 @@ typedef enum SubdivCCGAdjacencyType { * adjacent to a vertex, r_v1 and r_v2 will be the index of that vertex. */ SubdivCCGAdjacencyType BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(const SubdivCCG *subdiv_ccg, const SubdivCCGCoord *coord, - const MLoop *mloop, - const MPoly *mpoly, + const struct MLoop *mloop, + const struct MPoly *mpoly, int *r_v1, int *r_v2); diff --git a/source/blender/blenkernel/BKE_subsurf.h b/source/blender/blenkernel/BKE_subsurf.h index 5dd2935ad0d..f32a78c3015 100644 --- a/source/blender/blenkernel/BKE_subsurf.h +++ b/source/blender/blenkernel/BKE_subsurf.h @@ -61,27 +61,6 @@ int BKE_ccg_gridsize(int level); */ int BKE_ccg_factor(int low_level, int high_level); -/** - * Translate #GridHidden into the #ME_HIDE flag for MVerts. Assumes - * vertices are in the order output by #ccgDM_copyFinalVertArray. - */ -void subsurf_copy_grid_hidden(struct DerivedMesh *dm, - const struct MPoly *mpoly, - struct MVert *mvert, - const struct MDisps *mdisps); - -/** - * Translate #GridPaintMask into vertex paint masks. Assumes vertices - * are in the order output by #ccgDM_copyFinalVertArray. - */ -void subsurf_copy_grid_paint_mask(struct DerivedMesh *dm, - const struct MPoly *mpoly, - float *paint_mask, - const struct GridPaintMask *grid_paint_mask); - -bool subsurf_has_edges(struct DerivedMesh *dm); -bool subsurf_has_faces(struct DerivedMesh *dm); - typedef enum MultiresModifiedFlags { /* indicates the grids have been sculpted on, so MDisps * have to be updated */ diff --git a/source/blender/blenkernel/BKE_tracking.h b/source/blender/blenkernel/BKE_tracking.h index 29eb180a2ab..3f6d32e2f70 100644 --- a/source/blender/blenkernel/BKE_tracking.h +++ b/source/blender/blenkernel/BKE_tracking.h @@ -28,7 +28,22 @@ struct Scene; struct bGPDlayer; struct rcti; -/* **** Common functions **** */ +/* -------------------------------------------------------------------- + * Common types and constants. + */ + +typedef enum eTrackArea { + TRACK_AREA_POINT = (1 << 0), + TRACK_AREA_PAT = (1 << 1), + TRACK_AREA_SEARCH = (1 << 2), + + TRACK_AREA_NONE = 0, + TRACK_AREA_ALL = (TRACK_AREA_POINT | TRACK_AREA_PAT | TRACK_AREA_SEARCH), +} eTrackArea; + +/* -------------------------------------------------------------------- + * Common functions. + */ /** * Free tracking structure, only frees structure contents @@ -84,7 +99,10 @@ void BKE_tracking_get_projection_matrix(struct MovieTracking *tracking, int winy, float mat[4][4]); -/* **** Clipboard **** */ +/* -------------------------------------------------------------------- + * Clipboard. + */ + /** * Free clipboard by freeing memory used by all tracks in it. */ @@ -204,15 +222,21 @@ bool BKE_tracking_track_has_marker_at_frame(struct MovieTrackingTrack *track, in bool BKE_tracking_track_has_enabled_marker_at_frame(struct MovieTrackingTrack *track, int framenr); /** - * Clear track's path: - * - * - If action is #TRACK_CLEAR_REMAINED path from `ref_frame+1` up to end will be clear. - * - If action is #TRACK_CLEAR_UPTO path from the beginning up to `ref_frame-1` will be clear. - * - If action is #TRACK_CLEAR_ALL only marker at frame ref_frame will remain. + * Clear track's path. * * \note frame number should be in clip space, not scene space. */ -void BKE_tracking_track_path_clear(struct MovieTrackingTrack *track, int ref_frame, int action); +typedef enum eTrackClearAction { + /* Clear path from `ref_frame+1` up to the . */ + TRACK_CLEAR_UPTO, + /* Clear path from the beginning up to `ref_frame-1`. */ + TRACK_CLEAR_REMAINED, + /* Only marker at frame `ref_frame` will remain. */ + TRACK_CLEAR_ALL, +} eTrackClearAction; +void BKE_tracking_track_path_clear(struct MovieTrackingTrack *track, + int ref_frame, + eTrackClearAction action); void BKE_tracking_tracks_join(struct MovieTracking *tracking, struct MovieTrackingTrack *dst_track, @@ -240,7 +264,9 @@ float BKE_tracking_track_get_weight_for_marker(struct MovieClip *clip, struct MovieTrackingTrack *track, struct MovieTrackingMarker *marker); -/* Selection */ +/* -------------------------------------------------------------------- + * Selection. + */ /** * \param area: which part of marker should be selected. see TRACK_AREA_* constants. @@ -252,12 +278,43 @@ void BKE_tracking_track_select(struct ListBase *tracksbase, void BKE_tracking_track_deselect(struct MovieTrackingTrack *track, int area); void BKE_tracking_tracks_deselect_all(struct ListBase *tracksbase); -/* **** Marker **** */ +/* -------------------------------------------------------------------- + * Marker. + */ + struct MovieTrackingMarker *BKE_tracking_marker_insert(struct MovieTrackingTrack *track, struct MovieTrackingMarker *marker); void BKE_tracking_marker_delete(struct MovieTrackingTrack *track, int framenr); -void BKE_tracking_marker_clamp(struct MovieTrackingMarker *marker, int event); +/** + * If the pattern area is outside of the search area its position will be modified in a way that it + * is within the pattern is within the search area. + * + * If the pattern area is already within the search area nothing happens. + * + * If the pattern area is bigger than the search area the behavior is undefined. + * + * Search area is never modified. + */ +void BKE_tracking_marker_clamp_pattern_position(struct MovieTrackingMarker *marker); + +/** + * If the search size is such that pattern area is (partially) outside of the search area make the + * search area bigger so that the pattern is within the search area. + * + * Pattern area is never modified. + */ +void BKE_tracking_marker_clamp_search_size(struct MovieTrackingMarker *marker); + +/** + * If the search position is such that pattern area is (partially) outside of the search area move + * the search area so that the pattern is within the search area. + * + * If the search area is smaller than the pattern the behavior is undefined. + * + * Pattern area is never modified. + */ +void BKE_tracking_marker_clamp_search_position(struct MovieTrackingMarker *marker); /** * Get marker closest to the given frame number. @@ -296,7 +353,10 @@ void BKE_tracking_marker_get_subframe_position(struct MovieTrackingTrack *track, float framenr, float pos[2]); -/* **** Plane Track **** */ +/* -------------------------------------------------------------------- + * Plane track. + */ + /** * Creates new plane track out of selected point tracks. */ @@ -342,7 +402,10 @@ void BKE_tracking_plane_tracks_replace_point_track(struct MovieTracking *trackin struct MovieTrackingTrack *old_track, struct MovieTrackingTrack *new_track); -/* **** Plane Marker **** */ +/* -------------------------------------------------------------------- + * Plane marker. + */ + struct MovieTrackingPlaneMarker *BKE_tracking_plane_marker_insert( struct MovieTrackingPlaneTrack *plane_track, struct MovieTrackingPlaneMarker *plane_marker); void BKE_tracking_plane_marker_delete(struct MovieTrackingPlaneTrack *plane_track, int framenr); @@ -368,7 +431,10 @@ void BKE_tracking_plane_marker_get_subframe_corners(struct MovieTrackingPlaneTra float framenr, float corners[4][2]); -/* **** Object **** */ +/* -------------------------------------------------------------------- + * Object. + */ + struct MovieTrackingObject *BKE_tracking_object_add(struct MovieTracking *tracking, const char *name); bool BKE_tracking_object_delete(struct MovieTracking *tracking, @@ -390,7 +456,10 @@ struct ListBase *BKE_tracking_object_get_plane_tracks(struct MovieTracking *trac struct MovieTrackingReconstruction *BKE_tracking_object_get_reconstruction( struct MovieTracking *tracking, struct MovieTrackingObject *object); -/* **** Camera **** */ +/* -------------------------------------------------------------------- + * Camera. + */ + /** * Converts principal offset from center to offset of blender's camera. */ @@ -409,7 +478,10 @@ void BKE_tracking_camera_get_reconstructed_interpolate(struct MovieTracking *tra float framenr, float mat[4][4]); -/* **** Distortion/Undistortion **** */ +/* -------------------------------------------------------------------- + * (Un)distortion. + */ + struct MovieDistortion *BKE_tracking_distortion_new(struct MovieTracking *tracking, int calibration_width, int calibration_height); @@ -463,7 +535,10 @@ void BKE_tracking_max_distortion_delta_across_bound(struct MovieTracking *tracki bool undistort, float delta[2]); -/* **** Image sampling **** */ +/* -------------------------------------------------------------------- + * Image sampling. + */ + struct ImBuf *BKE_tracking_sample_pattern(int frame_width, int frame_height, struct ImBuf *search_ib, @@ -493,7 +568,9 @@ struct ImBuf *BKE_tracking_get_search_imbuf(struct ImBuf *ibuf, void BKE_tracking_disable_channels( struct ImBuf *ibuf, bool disable_red, bool disable_green, bool disable_blue, bool grayscale); -/* **** 2D tracking **** */ +/* -------------------------------------------------------------------- + * 2D tracking. + */ /** * Refine marker's position using previously known keyframe. @@ -505,7 +582,9 @@ void BKE_tracking_refine_marker(struct MovieClip *clip, struct MovieTrackingMarker *marker, bool backwards); -/* *** 2D auto track *** */ +/* -------------------------------------------------------------------- + * 2D tracking using auto-track pipeline. + */ struct AutoTrackContext *BKE_autotrack_context_new(struct MovieClip *clip, struct MovieClipUser *user, @@ -517,7 +596,9 @@ void BKE_autotrack_context_sync_user(struct AutoTrackContext *context, struct Mo void BKE_autotrack_context_finish(struct AutoTrackContext *context); void BKE_autotrack_context_free(struct AutoTrackContext *context); -/* **** Plane tracking **** */ +/* -------------------------------------------------------------------- + * Plane tracking. + */ /** * \note frame number should be in clip space, not scene space. @@ -530,7 +611,9 @@ void BKE_tracking_homography_between_two_quads(/*const*/ float reference_corners /*const*/ float corners[4][2], float H[3][3]); -/* **** Camera solving **** */ +/* -------------------------------------------------------------------- + * Camera solving. + */ /** * Perform early check on whether everything is fine to start reconstruction. @@ -617,7 +700,9 @@ void BKE_tracking_detect_harris(struct MovieTracking *tracking, struct bGPDlayer *layer, bool place_outside_layer); -/* **** 2D stabilization **** */ +/* -------------------------------------------------------------------- + * 2D stabilization. + */ /** * Get stabilization data (translation, scaling and angle) for a given frame. @@ -686,7 +771,9 @@ void BKE_tracking_dopesheet_tag_update(struct MovieTracking *tracking); */ void BKE_tracking_dopesheet_update(struct MovieTracking *tracking); -/* **** Query/search **** */ +/* -------------------------------------------------------------------- + * Query and search. + */ /** * \note Returns NULL if the track comes from camera object,. @@ -722,7 +809,9 @@ void BKE_tracking_get_rna_path_prefix_for_plane_track( char *rna_path, size_t rna_path_len); -/* **** Utility macros **** */ +/* -------------------------------------------------------------------- + * Utility macros. + */ #define TRACK_SELECTED(track) \ ((track)->flag & SELECT || (track)->pat_flag & SELECT || (track)->search_flag & SELECT) @@ -745,22 +834,6 @@ void BKE_tracking_get_rna_path_prefix_for_plane_track( (((marker)->flag & MARKER_DISABLED) == 0 || ((sc)->flag & SC_HIDE_DISABLED) == 0 || \ ((sc)->clip->tracking.act_track == track)) -#define TRACK_CLEAR_UPTO 0 -#define TRACK_CLEAR_REMAINED 1 -#define TRACK_CLEAR_ALL 2 - -#define CLAMP_PAT_DIM 1 -#define CLAMP_PAT_POS 2 -#define CLAMP_SEARCH_DIM 3 -#define CLAMP_SEARCH_POS 4 - -#define TRACK_AREA_NONE -1 -#define TRACK_AREA_POINT 1 -#define TRACK_AREA_PAT 2 -#define TRACK_AREA_SEARCH 4 - -#define TRACK_AREA_ALL (TRACK_AREA_POINT | TRACK_AREA_PAT | TRACK_AREA_SEARCH) - #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index a57d4d0a2bf..0b4f81df452 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -117,6 +117,7 @@ set(SRC intern/curveprofile.cc intern/curves.cc intern/curves_geometry.cc + intern/curves_utils.cc intern/customdata.cc intern/customdata_file.c intern/data_transfer.c @@ -242,8 +243,8 @@ set(SRC intern/particle_child.c intern/particle_distribute.c intern/particle_system.c - intern/pbvh.cc intern/pbvh.c + intern/pbvh.cc intern/pbvh_bmesh.c intern/pbvh_pixels.cc intern/pointcache.c @@ -356,6 +357,7 @@ set(SRC BKE_curveprofile.h BKE_curves.h BKE_curves.hh + BKE_curves_utils.hh BKE_customdata.h BKE_customdata_file.h BKE_data_transfer.h diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 7ca76e72839..fb44fc46531 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -423,21 +423,6 @@ static void mesh_set_only_copy(Mesh *mesh, const CustomData_MeshMasks *mask) #endif } -void DM_add_vert_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer) -{ - CustomData_add_layer(&dm->vertData, type, alloctype, layer, dm->numVertData); -} - -void DM_add_edge_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer) -{ - CustomData_add_layer(&dm->edgeData, type, alloctype, layer, dm->numEdgeData); -} - -void DM_add_poly_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer) -{ - CustomData_add_layer(&dm->polyData, type, alloctype, layer, dm->numPolyData); -} - void *DM_get_vert_data_layer(DerivedMesh *dm, int type) { if (type == CD_MVERT) { diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index b886722676b..94b85e42f96 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -2868,7 +2868,7 @@ static void nlastrip_evaluate_meta(const int evaluation_mode, /* Assert currently supported modes. If new mode added, then assertion marks potentially missed * area. * - * Note: In the future if support is ever added to metastrips to support nested tracks, then + * NOTE: In the future if support is ever added to metastrips to support nested tracks, then * STRIP_EVAL_BLEND and STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT cases are no longer * equivalent. The output of nlastrips_ctime_get_strip() may return a list of strips. The only * case difference should be the evaluation order. diff --git a/source/blender/blenkernel/intern/anonymous_attribute.cc b/source/blender/blenkernel/intern/anonymous_attribute.cc index 6ce6bee547c..636e0af0edf 100644 --- a/source/blender/blenkernel/intern/anonymous_attribute.cc +++ b/source/blender/blenkernel/intern/anonymous_attribute.cc @@ -41,7 +41,7 @@ static std::string get_new_internal_name() { static std::atomic<int> index = 0; const int next_index = index.fetch_add(1); - return "anonymous_attribute_" + std::to_string(next_index); + return ".a_" + std::to_string(next_index); } AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name) diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index 8d3649fef08..031d3647878 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -61,7 +61,7 @@ static CLG_LogRef LOG = {"bke.appdir"}; static struct { /** Full path to program executable. */ - char program_filename[FILE_MAX]; + char program_filepath[FILE_MAX]; /** Full path to directory in which executable is located. */ char program_dirname[FILE_MAX]; /** Persistent temporary directory (defined by the preferences or OS). */ @@ -860,14 +860,14 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name) void BKE_appdir_program_path_init(const char *argv0) { - where_am_i(g_app.program_filename, sizeof(g_app.program_filename), argv0); - BLI_split_dir_part(g_app.program_filename, g_app.program_dirname, sizeof(g_app.program_dirname)); + where_am_i(g_app.program_filepath, sizeof(g_app.program_filepath), argv0); + BLI_split_dir_part(g_app.program_filepath, g_app.program_dirname, sizeof(g_app.program_dirname)); } const char *BKE_appdir_program_path(void) { - BLI_assert(g_app.program_filename[0]); - return g_app.program_filename; + BLI_assert(g_app.program_filepath[0]); + return g_app.program_filepath; } const char *BKE_appdir_program_dir(void) diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 11f36e32b74..81eb1786322 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -318,7 +318,7 @@ TEST_F(AssetCatalogTest, load_catalog_path_backslashes) const AssetCatalog *found_by_id = service.find_catalog(UUID_POSES_ELLIE_BACKSLASHES); ASSERT_NE(nullptr, found_by_id); EXPECT_EQ(AssetCatalogPath("character/Ellie/backslashes"), found_by_id->path) - << "Backslashes should be normalised when loading from disk."; + << "Backslashes should be normalized when loading from disk."; EXPECT_EQ(StringRefNull("Windows For Life!"), found_by_id->simple_name); const AssetCatalog *found_by_path = service.find_catalog_by_path("character/Ellie/backslashes"); diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c index 0cb0704ff80..7f93eb7b393 100644 --- a/source/blender/blenkernel/intern/attribute.c +++ b/source/blender/blenkernel/intern/attribute.c @@ -75,9 +75,9 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM]) case ID_CV: { Curves *curves = (Curves *)id; info[ATTR_DOMAIN_POINT].customdata = &curves->geometry.point_data; - info[ATTR_DOMAIN_POINT].length = curves->geometry.point_size; + info[ATTR_DOMAIN_POINT].length = curves->geometry.point_num; info[ATTR_DOMAIN_CURVE].customdata = &curves->geometry.curve_data; - info[ATTR_DOMAIN_CURVE].length = curves->geometry.curve_size; + info[ATTR_DOMAIN_CURVE].length = curves->geometry.curve_num; break; } default: diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index d33b64c493b..e86bac21084 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -184,16 +184,16 @@ static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data, const CustomDataType data_type, - const int domain_size, + const int domain_num, const AttributeInit &initializer) { switch (initializer.type) { case AttributeInit::Type::Default: { - void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); + void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_num); return data != nullptr; } case AttributeInit::Type::VArray: { - void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); + void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_num); if (data == nullptr) { return false; } @@ -204,7 +204,7 @@ static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data case AttributeInit::Type::MoveArray: { void *source_data = static_cast<const AttributeInitMove &>(initializer).data; void *data = CustomData_add_layer( - &custom_data, data_type, CD_ASSIGN, source_data, domain_size); + &custom_data, data_type, CD_ASSIGN, source_data, domain_num); if (data == nullptr) { MEM_freeN(source_data); return false; @@ -221,35 +221,35 @@ static void *add_generic_custom_data_layer(CustomData &custom_data, const CustomDataType data_type, const eCDAllocType alloctype, void *layer_data, - const int domain_size, + const int domain_num, const AttributeIDRef &attribute_id) { if (attribute_id.is_named()) { char attribute_name_c[MAX_NAME]; attribute_id.name().copy(attribute_name_c); return CustomData_add_layer_named( - &custom_data, data_type, alloctype, layer_data, domain_size, attribute_name_c); + &custom_data, data_type, alloctype, layer_data, domain_num, attribute_name_c); } const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id(); return CustomData_add_layer_anonymous( - &custom_data, data_type, alloctype, layer_data, domain_size, &anonymous_id); + &custom_data, data_type, alloctype, layer_data, domain_num, &anonymous_id); } static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attribute_id, CustomData &custom_data, const CustomDataType data_type, - const int domain_size, + const int domain_num, const AttributeInit &initializer) { switch (initializer.type) { case AttributeInit::Type::Default: { void *data = add_generic_custom_data_layer( - custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); + custom_data, data_type, CD_DEFAULT, nullptr, domain_num, attribute_id); return data != nullptr; } case AttributeInit::Type::VArray: { void *data = add_generic_custom_data_layer( - custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); + custom_data, data_type, CD_DEFAULT, nullptr, domain_num, attribute_id); if (data == nullptr) { return false; } @@ -260,7 +260,7 @@ static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attr case AttributeInit::Type::MoveArray: { void *source_data = static_cast<const AttributeInitMove &>(initializer).data; void *data = add_generic_custom_data_layer( - custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_id); + custom_data, data_type, CD_ASSIGN, source_data, domain_num, attribute_id); if (data == nullptr) { MEM_freeN(source_data); return false; @@ -303,8 +303,8 @@ GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const GeometryComponent return {}; } - const int domain_size = component.attribute_domain_size(domain_); - return as_read_attribute_(data, domain_size); + const int domain_num = component.attribute_domain_num(domain_); + return as_read_attribute_(data, domain_num); } WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( @@ -317,7 +317,7 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( if (custom_data == nullptr) { return {}; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); void *data; if (stored_as_named_attribute_) { @@ -333,10 +333,10 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( void *new_data; if (stored_as_named_attribute_) { new_data = CustomData_duplicate_referenced_layer_named( - custom_data, stored_type_, name_.c_str(), domain_size); + custom_data, stored_type_, name_.c_str(), domain_num); } else { - new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_size); + new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_num); } if (data != new_data) { @@ -353,7 +353,7 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( }; } - return {as_write_attribute_(data, domain_size), domain_, std::move(tag_modified_fn)}; + return {as_write_attribute_(data, domain_num), domain_, std::move(tag_modified_fn)}; } bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) const @@ -366,7 +366,7 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co return {}; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); int layer_index; if (stored_as_named_attribute_) { for (const int i : IndexRange(custom_data->totlayer)) { @@ -381,7 +381,7 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co } const bool delete_success = CustomData_free_layer( - custom_data, stored_type_, domain_size, layer_index); + custom_data, stored_type_, domain_num, layer_index); if (delete_success) { if (custom_data_access_.update_custom_data_pointers) { custom_data_access_.update_custom_data_pointers(component); @@ -401,7 +401,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, return false; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); bool success; if (stored_as_named_attribute_) { if (CustomData_get_layer_named(custom_data, data_type_, name_.c_str())) { @@ -409,7 +409,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, return false; } success = add_custom_data_layer_from_attribute_init( - name_, *custom_data, stored_type_, domain_size, initializer); + name_, *custom_data, stored_type_, domain_num, initializer); } else { if (CustomData_get_layer(custom_data, stored_type_) != nullptr) { @@ -417,7 +417,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, return false; } success = add_builtin_type_custom_data_layer_from_init( - *custom_data, stored_type_, domain_size, initializer); + *custom_data, stored_type_, domain_num, initializer); } if (success) { if (custom_data_access_.update_custom_data_pointers) { @@ -446,7 +446,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( if (custom_data == nullptr) { return {}; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; @@ -455,7 +455,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( if (type == nullptr) { continue; } - GSpan data{*type, layer.data, domain_size}; + GSpan data{*type, layer.data, domain_num}; return {GVArray::ForSpan(data), domain_}; } return {}; @@ -468,24 +468,23 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( if (custom_data == nullptr) { return {}; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; } if (attribute_id.is_named()) { - CustomData_duplicate_referenced_layer_named( - custom_data, layer.type, layer.name, domain_size); + CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_num); } else { CustomData_duplicate_referenced_layer_anonymous( - custom_data, layer.type, &attribute_id.anonymous_id(), domain_size); + custom_data, layer.type, &attribute_id.anonymous_id(), domain_num); } const CPPType *type = custom_data_type_to_cpp_type((CustomDataType)layer.type); if (type == nullptr) { continue; } - GMutableSpan data{*type, layer.data, domain_size}; + GMutableSpan data{*type, layer.data, domain_num}; return {GVMutableArray::ForSpan(data), domain_}; } return {}; @@ -498,12 +497,12 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, if (custom_data == nullptr) { return false; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); for (const int i : IndexRange(custom_data->totlayer)) { const CustomDataLayer &layer = custom_data->layers[i]; if (this->type_is_supported((CustomDataType)layer.type) && custom_data_layer_matches_attribute_id(layer, attribute_id)) { - CustomData_free_layer(custom_data, layer.type, domain_size, i); + CustomData_free_layer(custom_data, layer.type, domain_num, i); return true; } } @@ -531,9 +530,9 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component, return false; } } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); add_custom_data_layer_from_attribute_init( - attribute_id, *custom_data, data_type, domain_size, initializer); + attribute_id, *custom_data, data_type, domain_num, initializer); return true; } @@ -567,8 +566,8 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { if (layer.type == stored_type_) { if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { - const int domain_size = component.attribute_domain_size(domain_); - return {as_read_attribute_(layer.data, domain_size), domain_}; + const int domain_num = component.attribute_domain_num(domain_); + return {as_read_attribute_(layer.data, domain_num), domain_}; } } } @@ -585,16 +584,16 @@ WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { if (layer.type == stored_type_) { if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); void *data_old = layer.data; void *data_new = CustomData_duplicate_referenced_layer_named( - custom_data, stored_type_, layer.name, domain_size); + custom_data, stored_type_, layer.name, domain_num); if (data_old != data_new) { if (custom_data_access_.update_custom_data_pointers) { custom_data_access_.update_custom_data_pointers(component); } } - return {as_write_attribute_(layer.data, domain_size), domain_}; + return {as_write_attribute_(layer.data, domain_num), domain_}; } } } @@ -612,8 +611,8 @@ bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, const CustomDataLayer &layer = custom_data->layers[i]; if (layer.type == stored_type_) { if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { - const int domain_size = component.attribute_domain_size(domain_); - CustomData_free_layer(custom_data, stored_type_, domain_size, i); + const int domain_num = component.attribute_domain_num(domain_); + CustomData_free_layer(custom_data, stored_type_, domain_num, i); if (custom_data_access_.update_custom_data_pointers) { custom_data_access_.update_custom_data_pointers(component); } @@ -702,9 +701,9 @@ GVArray CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id, std::optional<GSpan> attribute = this->get_for_read(attribute_id); if (!attribute) { - const int domain_size = this->size_; + const int domain_num = this->size_; return GVArray::ForSingle( - *type, domain_size, (default_value == nullptr) ? type->default_value() : default_value); + *type, domain_num, (default_value == nullptr) ? type->default_value() : default_value); } if (attribute->type() == *type) { @@ -822,7 +821,7 @@ bool GeometryComponent::attribute_domain_supported(const AttributeDomain domain) return providers->supported_domains().contains(domain); } -int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain)) const +int GeometryComponent::attribute_domain_num(const AttributeDomain UNUSED(domain)) const { return 0; } @@ -1157,8 +1156,8 @@ blender::GVArray GeometryComponent::attribute_get_for_read(const AttributeIDRef if (default_value == nullptr) { default_value = type->default_value(); } - const int domain_size = this->attribute_domain_size(domain); - return blender::GVArray::ForSingle(*type, domain_size, default_value); + const int domain_num = this->attribute_domain_num(domain); + return blender::GVArray::ForSingle(*type, domain_num, default_value); } class GVMutableAttribute_For_OutputAttribute : public blender::GVArrayImpl_For_GSpan { @@ -1267,10 +1266,10 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); if (!attribute) { if (default_value) { - const int64_t domain_size = component.attribute_domain_size(domain); + const int64_t domain_num = component.attribute_domain_num(domain); component.attribute_try_create_builtin( attribute_name, - AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_size, default_value))); + AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_num, default_value))); } else { component.attribute_try_create_builtin(attribute_name, AttributeInitDefault()); @@ -1301,7 +1300,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, ignore_old_values); } - const int domain_size = component.attribute_domain_size(domain); + const int domain_num = component.attribute_domain_num(domain); WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_id); if (!attribute) { @@ -1310,7 +1309,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, attribute_id, domain, data_type, - AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_size, default_value))); + AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_num, default_value))); } else { component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault()); @@ -1333,8 +1332,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing * attribute after processing is done. */ - void *data = MEM_mallocN_aligned( - cpp_type->size() * domain_size, cpp_type->alignment(), __func__); + void *data = MEM_mallocN_aligned(cpp_type->size() * domain_num, cpp_type->alignment(), __func__); if (ignore_old_values) { /* This does nothing for trivially constructible types, but is necessary for correctness. */ cpp_type->default_construct_n(data, domain); @@ -1343,10 +1341,10 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, /* Fill the temporary array with values from the existing attribute. */ GVArray old_varray = component.attribute_get_for_read( attribute_id, domain, data_type, default_value); - old_varray.materialize_to_uninitialized(IndexRange(domain_size), data); + old_varray.materialize_to_uninitialized(IndexRange(domain_num), data); } GVMutableArray varray = GVMutableArray::For<GVMutableAttribute_For_OutputAttribute>( - GMutableSpan{*cpp_type, data, domain_size}, component, attribute_id); + GMutableSpan{*cpp_type, data, domain_num}, component, attribute_id); return OutputAttribute(std::move(varray), domain, save_output_attribute, true); } @@ -1429,7 +1427,7 @@ GVArray IDAttributeFieldInput::get_varray_for_context(const GeometryComponent &c const StringRef name = get_random_id_attribute_name(domain); GVArray attribute = component.attribute_try_get_for_read(name, domain, CD_PROP_INT32); if (attribute) { - BLI_assert(attribute.size() == component.attribute_domain_size(domain)); + BLI_assert(attribute.size() == component.attribute_domain_num(domain)); return attribute; } diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 8c021ed0e21..f0f47cb7a11 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -172,8 +172,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { */ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { private: - using AsReadAttribute = GVArray (*)(const void *data, int domain_size); - using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_size); + using AsReadAttribute = GVArray (*)(const void *data, int domain_num); + using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_num); const AttributeDomain domain_; const CustomDataType attribute_type_; const CustomDataType stored_type_; @@ -207,14 +207,14 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final; }; -template<typename T> GVArray make_array_read_attribute(const void *data, const int domain_size) +template<typename T> GVArray make_array_read_attribute(const void *data, const int domain_num) { - return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size)); + return VArray<T>::ForSpan(Span<T>((const T *)data, domain_num)); } -template<typename T> GVMutableArray make_array_write_attribute(void *data, const int domain_size) +template<typename T> GVMutableArray make_array_write_attribute(void *data, const int domain_num) { - return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size)); + return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_num)); } /** @@ -226,8 +226,8 @@ template<typename T> GVMutableArray make_array_write_attribute(void *data, const * if the stored type is the same as the attribute type. */ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { - using AsReadAttribute = GVArray (*)(const void *data, int domain_size); - using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_size); + using AsReadAttribute = GVArray (*)(const void *data, int domain_num); + using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_num); using UpdateOnRead = void (*)(const GeometryComponent &component); using UpdateOnWrite = void (*)(GeometryComponent &component); const CustomDataType stored_type_; diff --git a/source/blender/blenkernel/intern/blender_copybuffer.c b/source/blender/blenkernel/intern/blender_copybuffer.c index f93b3efa8dd..4b507beb6b3 100644 --- a/source/blender/blenkernel/intern/blender_copybuffer.c +++ b/source/blender/blenkernel/intern/blender_copybuffer.c @@ -96,7 +96,7 @@ bool BKE_copybuffer_read(Main *bmain_dst, ReportList *reports, const uint64_t id_types_mask) { - /* Note: No recursive append here (no `BLO_LIBLINK_APPEND_RECURSIVE`), external linked data + /* NOTE: No recursive append here (no `BLO_LIBLINK_APPEND_RECURSIVE`), external linked data * should remain linked. */ const int flag = 0; const int id_tag_extra = 0; @@ -132,7 +132,7 @@ int BKE_copybuffer_paste(bContext *C, View3D *v3d = CTX_wm_view3d(C); /* may be NULL. */ const int id_tag_extra = 0; - /* Note: No recursive append here, external linked data should remain linked. */ + /* NOTE: No recursive append here, external linked data should remain linked. */ BLI_assert((flag & BLO_LIBLINK_APPEND_RECURSIVE) == 0); struct LibraryLink_Params liblink_params; diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c index c32941ccbc9..a5096b4f9eb 100644 --- a/source/blender/blenkernel/intern/blender_undo.c +++ b/source/blender/blenkernel/intern/blender_undo.c @@ -65,7 +65,7 @@ bool BKE_memfile_undo_decode(MemFileUndoData *mfu, if (UNDO_DISK) { const struct BlendFileReadParams params = {0}; BlendFileReadReport bf_reports = {.reports = NULL}; - struct BlendFileData *bfd = BKE_blendfile_read(mfu->filename, ¶ms, &bf_reports); + struct BlendFileData *bfd = BKE_blendfile_read(mfu->filepath, ¶ms, &bf_reports); if (bfd != NULL) { BKE_blendfile_read_setup(C, bfd, ¶ms, &bf_reports); success = true; @@ -108,20 +108,20 @@ MemFileUndoData *BKE_memfile_undo_encode(Main *bmain, MemFileUndoData *mfu_prev) /* disk save version */ if (UNDO_DISK) { static int counter = 0; - char filename[FILE_MAX]; + char filepath[FILE_MAX]; char numstr[32]; - /* Calculate current filename. */ + /* Calculate current filepath. */ counter++; counter = counter % U.undosteps; BLI_snprintf(numstr, sizeof(numstr), "%d.blend", counter); - BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_session(), numstr); + BLI_join_dirfile(filepath, sizeof(filepath), BKE_tempdir_session(), numstr); /* success = */ /* UNUSED */ BLO_write_file( - bmain, filename, fileflags, &(const struct BlendFileWriteParams){0}, NULL); + bmain, filepath, fileflags, &(const struct BlendFileWriteParams){0}, NULL); - BLI_strncpy(mfu->filename, filename, sizeof(mfu->filename)); + BLI_strncpy(mfu->filepath, filepath, sizeof(mfu->filepath)); } else { MemFile *prevfile = (mfu_prev) ? &(mfu_prev->memfile) : NULL; diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c index f9eea52360e..be4b671ef75 100644 --- a/source/blender/blenkernel/intern/blendfile_link_append.c +++ b/source/blender/blenkernel/intern/blendfile_link_append.c @@ -400,7 +400,7 @@ typedef struct LooseDataInstantiateContext { static bool object_in_any_scene(Main *bmain, Object *ob) { LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { - /* #BKE_scene_has_object checks bases cache of the scenes' viewlayer, not actual content of + /* #BKE_scene_has_object checks bases cache of the scenes' view-layer, not actual content of * their collections. */ if (BKE_collection_has_object_recursive(sce->master_collection, ob)) { return true; @@ -1125,7 +1125,7 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList * &LOG, "Unexpected unset append action for '%s' ID, assuming 'keep link'", id->name); break; default: - BLI_assert(0); + BLI_assert_unreachable(); } if (local_appended_new_id != NULL) { @@ -1566,7 +1566,7 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context, } if (GS(old_id->name) == ID_KE) { - /* Shape Keys are handled as part of their owning obdata (see below). This implies thar + /* Shape Keys are handled as part of their owning obdata (see below). This implies that * there is no way to know when the old pointer gets invalid, so just clear it immediately. */ item->userdata = NULL; diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 3f243d04965..083d5af063a 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -1559,6 +1559,7 @@ void BKE_brush_init_curves_sculpt_settings(Brush *brush) } BrushCurvesSculptSettings *settings = brush->curves_sculpt_settings; settings->add_amount = 1; + settings->points_per_curve = 8; settings->minimum_length = 0.01f; settings->curve_length = 0.3f; } diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc index 37b0875db67..4d95cdd1e02 100644 --- a/source/blender/blenkernel/intern/bvhutils.cc +++ b/source/blender/blenkernel/intern/bvhutils.cc @@ -1212,10 +1212,11 @@ static BLI_bitmap *looptri_no_hidden_map_get(const MPoly *mpoly, int looptri_no_hidden_len = 0; int looptri_iter = 0; - const MPoly *mp = mpoly; + int i_poly = 0; while (looptri_iter != looptri_len) { - int mp_totlooptri = mp->totloop - 2; - if (mp->flag & ME_HIDE) { + int mp_totlooptri = mpoly[i_poly].totloop - 2; + const MPoly &mp = mpoly[i_poly]; + if (mp.flag & ME_HIDE) { looptri_iter += mp_totlooptri; } else { @@ -1225,7 +1226,7 @@ static BLI_bitmap *looptri_no_hidden_map_get(const MPoly *mpoly, looptri_no_hidden_len++; } } - mp++; + i_poly++; } *r_looptri_active_len = looptri_no_hidden_len; diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 32925168437..b59e44aae8a 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -302,7 +302,7 @@ void BKE_camera_params_from_object(CameraParams *params, const Object *cam_ob) } void BKE_camera_params_from_view3d(CameraParams *params, - Depsgraph *depsgraph, + const Depsgraph *depsgraph, const View3D *v3d, const RegionView3D *rv3d) { diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 2d742a103af..93286751f92 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -65,18 +65,6 @@ static int cdDM_getNumEdges(DerivedMesh *dm) return dm->numEdgeData; } -static int cdDM_getNumTessFaces(DerivedMesh *dm) -{ - /* uncomment and add a breakpoint on the printf() - * to help debug tessfaces issues since BMESH merge. */ -#if 0 - if (dm->numTessFaceData == 0 && dm->numPolyData != 0) { - printf("%s: has no faces!\n"); - } -#endif - return dm->numTessFaceData; -} - static int cdDM_getNumLoops(DerivedMesh *dm) { return dm->numLoopData; @@ -173,7 +161,6 @@ static CDDerivedMesh *cdDM_create(const char *desc) dm->getNumVerts = cdDM_getNumVerts; dm->getNumEdges = cdDM_getNumEdges; - dm->getNumTessFaces = cdDM_getNumTessFaces; dm->getNumLoops = cdDM_getNumLoops; dm->getNumPolys = cdDM_getNumPolys; diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index 0d6a0c045a5..ab9a27a3996 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -632,7 +632,7 @@ static void cloth_apply_vgroup(ClothModifierData *clmd, Mesh *mesh) verts->flags &= ~(CLOTH_VERT_FLAG_PINNED | CLOTH_VERT_FLAG_NOSELFCOLL | CLOTH_VERT_FLAG_NOOBJCOLL); - MDeformVert *dvert = CustomData_get(&mesh->vdata, i, CD_MDEFORMVERT); + const MDeformVert *dvert = CustomData_get(&mesh->vdata, i, CD_MDEFORMVERT); if (dvert) { for (int j = 0; j < dvert->totweight; j++) { if (dvert->dw[j].def_nr == (clmd->sim_parms->vgroup_mass - 1)) { @@ -715,7 +715,7 @@ static bool cloth_from_object( int i = 0; MVert *mvert = NULL; ClothVertex *verts = NULL; - float(*shapekey_rest)[3] = NULL; + const float(*shapekey_rest)[3] = NULL; const float tnull[3] = {0, 0, 0}; /* If we have a clothObject, free it. */ @@ -1127,7 +1127,7 @@ static void cloth_update_springs(ClothModifierData *clmd) spring->lin_stiffness = (v1->bend_stiff + v2->bend_stiff) / 2.0f; } else if (spring->type == CLOTH_SPRING_TYPE_GOAL) { - /* Warning: Appending NEW goal springs does not work + /* WARNING: Appending NEW goal springs does not work * because implicit solver would need reset! */ /* Activate / Deactivate existing springs */ diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index c3d66d4463d..e4c46703f8a 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -1158,6 +1158,80 @@ bool BKE_curvemapping_RGBA_does_something(const CurveMapping *cumap) return false; } +void BKE_curvemapping_get_range_minimums(const CurveMapping *curve_mapping, float minimums[CM_TOT]) +{ + for (int i = 0; i < CM_TOT; i++) { + minimums[i] = curve_mapping->cm[i].mintable; + } +} + +void BKE_curvemapping_compute_range_dividers(const CurveMapping *curve_mapping, + float dividers[CM_TOT]) +{ + for (int i = 0; i < CM_TOT; i++) { + const CurveMap *curve_map = &curve_mapping->cm[i]; + dividers[i] = 1.0f / max_ff(1e-8f, curve_map->maxtable - curve_map->mintable); + } +} + +void BKE_curvemapping_compute_slopes(const CurveMapping *curve_mapping, + float start_slopes[CM_TOT], + float end_slopes[CM_TOT]) +{ + float range_dividers[CM_TOT]; + BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers); + for (int i = 0; i < CM_TOT; i++) { + const CurveMap *curve_map = &curve_mapping->cm[i]; + /* If extrapolation is not enabled, the slopes are horizontal. */ + if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) { + start_slopes[i] = 0.0f; + end_slopes[i] = 0.0f; + continue; + } + + if (curve_map->ext_in[0] != 0.0f) { + start_slopes[i] = curve_map->ext_in[1] / (curve_map->ext_in[0] * range_dividers[i]); + } + else { + start_slopes[i] = 1e8f; + } + + if (curve_map->ext_out[0] != 0.0f) { + end_slopes[i] = curve_map->ext_out[1] / (curve_map->ext_out[0] * range_dividers[i]); + } + else { + end_slopes[i] = 1e8f; + } + } +} + +bool BKE_curvemapping_is_map_identity(const CurveMapping *curve_mapping, int index) +{ + if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) { + return false; + } + const CurveMap *curve_map = &curve_mapping->cm[index]; + if (curve_map->maxtable - curve_map->mintable != 1.0f) { + return false; + } + if (curve_map->ext_in[0] != curve_map->ext_in[1]) { + return false; + } + if (curve_map->ext_out[0] != curve_map->ext_out[1]) { + return false; + } + if (curve_map->totpoint != 2) { + return false; + } + if (curve_map->curve[0].x != 0 || curve_map->curve[0].y != 0) { + return false; + } + if (curve_map->curve[1].x != 0 || curve_map->curve[1].y != 0) { + return false; + } + return true; +} + void BKE_curvemapping_init(CurveMapping *cumap) { int a; diff --git a/source/blender/blenkernel/intern/crazyspace.c b/source/blender/blenkernel/intern/crazyspace.c index 96389c44839..14e862c2377 100644 --- a/source/blender/blenkernel/intern/crazyspace.c +++ b/source/blender/blenkernel/intern/crazyspace.c @@ -13,8 +13,8 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BLI_bitmap.h" #include "BLI_linklist.h" -#include "BLI_math.h" #include "BLI_utildefines.h" #include "BKE_DerivedMesh.h" diff --git a/source/blender/blenkernel/intern/curve_catmull_rom.cc b/source/blender/blenkernel/intern/curve_catmull_rom.cc index 2db183eea3e..4b2174c912c 100644 --- a/source/blender/blenkernel/intern/curve_catmull_rom.cc +++ b/source/blender/blenkernel/intern/curve_catmull_rom.cc @@ -9,11 +9,11 @@ namespace blender::bke::curves::catmull_rom { -int calculate_evaluated_size(const int points_num, const bool cyclic, const int resolution) +int calculate_evaluated_num(const int points_num, const bool cyclic, const int resolution) { - const int eval_size = resolution * curve_segment_size(points_num, cyclic); + const int eval_num = resolution * curve_segment_num(points_num, cyclic); /* If the curve isn't cyclic, one last point is added to the final point. */ - return cyclic ? eval_size : eval_size + 1; + return cyclic ? eval_num : eval_num + 1; } /* Adapted from Cycles #catmull_rom_basis_eval function. */ @@ -46,7 +46,7 @@ static void interpolate_to_evaluated(const Span<T> src, MutableSpan<T> dst) { - BLI_assert(dst.size() == calculate_evaluated_size(src.size(), cyclic, resolution)); + BLI_assert(dst.size() == calculate_evaluated_num(src.size(), cyclic, resolution)); /* - First deal with one and two point curves need special attention. * - Then evaluate the first and last segment(s) whose control points need to wrap around diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 3d9dd3ecf31..dd2bd982506 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -117,7 +117,7 @@ float CurveEval::total_length() const return length; } -int CurveEval::total_control_point_size() const +int CurveEval::total_control_point_num() const { int count = 0; for (const SplinePtr &spline : this->splines()) { @@ -144,7 +144,7 @@ blender::Array<int> CurveEval::evaluated_point_offsets() const int offset = 0; for (const int i : splines_.index_range()) { offsets[i] = offset; - offset += splines_[i]->evaluated_points_size(); + offset += splines_[i]->evaluated_points_num(); } offsets.last() = offset; return offsets; @@ -373,15 +373,15 @@ static void copy_attributes_between_components(const GeometryComponent &src_comp }); } -std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves) +std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves_id) { CurveComponent src_component; - src_component.replace(&const_cast<Curves &>(curves), GeometryOwnershipType::ReadOnly); - const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( - curves.geometry); + src_component.replace(&const_cast<Curves &>(curves_id), GeometryOwnershipType::ReadOnly); + const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( + curves_id.geometry); - VArray<int> resolution = geometry.resolution(); - VArray<int8_t> normal_mode = geometry.normal_mode(); + VArray<int> resolution = curves.resolution(); + VArray<int8_t> normal_mode = curves.normal_mode(); VArray_Span<float> nurbs_weights{ src_component.attribute_get_for_read<float>("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)}; @@ -396,34 +396,34 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves) src_component.attribute_get_for_read<int8_t>("handle_type_left", ATTR_DOMAIN_POINT, 0)}; /* Create splines with the correct size and type. */ - VArray<int8_t> curve_types = geometry.curve_types(); + VArray<int8_t> curve_types = curves.curve_types(); std::unique_ptr<CurveEval> curve_eval = std::make_unique<CurveEval>(); for (const int curve_index : curve_types.index_range()) { - const IndexRange point_range = geometry.points_for_curve(curve_index); + const IndexRange points = curves.points_for_curve(curve_index); std::unique_ptr<Spline> spline; /* #CurveEval does not support catmull rom curves, so convert those to poly splines. */ switch (std::max<int8_t>(1, curve_types[curve_index])) { case CURVE_TYPE_POLY: { spline = std::make_unique<PolySpline>(); - spline->resize(point_range.size()); + spline->resize(points.size()); break; } case CURVE_TYPE_BEZIER: { std::unique_ptr<BezierSpline> bezier_spline = std::make_unique<BezierSpline>(); - bezier_spline->resize(point_range.size()); + bezier_spline->resize(points.size()); bezier_spline->set_resolution(resolution[curve_index]); - bezier_spline->handle_types_left().copy_from(handle_types_left.slice(point_range)); - bezier_spline->handle_types_right().copy_from(handle_types_right.slice(point_range)); + bezier_spline->handle_types_left().copy_from(handle_types_left.slice(points)); + bezier_spline->handle_types_right().copy_from(handle_types_right.slice(points)); spline = std::move(bezier_spline); break; } case CURVE_TYPE_NURBS: { std::unique_ptr<NURBSpline> nurb_spline = std::make_unique<NURBSpline>(); - nurb_spline->resize(point_range.size()); + nurb_spline->resize(points.size()); nurb_spline->set_resolution(resolution[curve_index]); - nurb_spline->weights().copy_from(nurbs_weights.slice(point_range)); + nurb_spline->weights().copy_from(nurbs_weights.slice(points)); nurb_spline->set_order(nurbs_orders[curve_index]); nurb_spline->knots_mode = static_cast<KnotsMode>(nurbs_knots_modes[curve_index]); @@ -463,14 +463,14 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves) Curves *curve_eval_to_curves(const CurveEval &curve_eval) { - Curves *curves = blender::bke::curves_new_nomain(curve_eval.total_control_point_size(), - curve_eval.splines().size()); + Curves *curves_id = blender::bke::curves_new_nomain(curve_eval.total_control_point_num(), + curve_eval.splines().size()); CurveComponent dst_component; - dst_component.replace(curves, GeometryOwnershipType::Editable); + dst_component.replace(curves_id, GeometryOwnershipType::Editable); - blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(curves->geometry); - geometry.offsets_for_write().copy_from(curve_eval.control_point_offsets()); - MutableSpan<int8_t> curve_types = geometry.curve_types_for_write(); + blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(curves_id->geometry); + curves.offsets_for_write().copy_from(curve_eval.control_point_offsets()); + MutableSpan<int8_t> curve_types = curves.curve_types_for_write(); OutputAttribute_Typed<int8_t> normal_mode = dst_component.attribute_try_get_for_output_only<int8_t>("normal_mode", ATTR_DOMAIN_CURVE); @@ -498,22 +498,22 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval) const Spline &spline = *curve_eval.splines()[curve_index]; curve_types[curve_index] = curve_eval.splines()[curve_index]->type(); normal_mode.as_span()[curve_index] = curve_eval.splines()[curve_index]->normal_mode; - const IndexRange point_range = geometry.points_for_curve(curve_index); + const IndexRange points = curves.points_for_curve(curve_index); switch (spline.type()) { case CURVE_TYPE_POLY: break; case CURVE_TYPE_BEZIER: { const BezierSpline &src = static_cast<const BezierSpline &>(spline); - handle_type_right.as_span().slice(point_range).copy_from(src.handle_types_right()); - handle_type_left.as_span().slice(point_range).copy_from(src.handle_types_left()); + handle_type_right.as_span().slice(points).copy_from(src.handle_types_right()); + handle_type_left.as_span().slice(points).copy_from(src.handle_types_left()); break; } case CURVE_TYPE_NURBS: { const NURBSpline &src = static_cast<const NURBSpline &>(spline); nurbs_knots_mode.as_span()[curve_index] = static_cast<int8_t>(src.knots_mode); nurbs_order.as_span()[curve_index] = src.order(); - nurbs_weight.as_span().slice(point_range).copy_from(src.weights()); + nurbs_weight.as_span().slice(points).copy_from(src.weights()); break; } case CURVE_TYPE_CATMULL_ROM: { @@ -523,7 +523,7 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval) } } - geometry.update_curve_types(); + curves.update_curve_types(); normal_mode.save(); nurbs_weight.save(); @@ -537,7 +537,7 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval) copy_attributes_between_components(src_component, dst_component, {}); - return curves; + return curves_id; } void CurveEval::assert_valid_point_attributes() const diff --git a/source/blender/blenkernel/intern/curve_nurbs.cc b/source/blender/blenkernel/intern/curve_nurbs.cc index 0114c0b45f4..cd6b64e9a03 100644 --- a/source/blender/blenkernel/intern/curve_nurbs.cc +++ b/source/blender/blenkernel/intern/curve_nurbs.cc @@ -10,10 +10,10 @@ namespace blender::bke::curves::nurbs { -bool check_valid_size_and_order(const int points_num, - const int8_t order, - const bool cyclic, - const KnotsMode knots_mode) +bool check_valid_num_and_order(const int points_num, + const int8_t order, + const bool cyclic, + const KnotsMode knots_mode) { if (points_num < order) { return false; @@ -29,19 +29,19 @@ bool check_valid_size_and_order(const int points_num, return true; } -int calculate_evaluated_size(const int points_num, - const int8_t order, - const bool cyclic, - const int resolution, - const KnotsMode knots_mode) +int calculate_evaluated_num(const int points_num, + const int8_t order, + const bool cyclic, + const int resolution, + const KnotsMode knots_mode) { - if (!check_valid_size_and_order(points_num, order, cyclic, knots_mode)) { - return 0; + if (!check_valid_num_and_order(points_num, order, cyclic, knots_mode)) { + return points_num; } - return resolution * curve_segment_size(points_num, cyclic); + return resolution * curve_segment_num(points_num, cyclic); } -int knots_size(const int points_num, const int8_t order, const bool cyclic) +int knots_num(const int points_num, const int8_t order, const bool cyclic) { if (cyclic) { return points_num + order * 2 - 1; @@ -55,7 +55,7 @@ void calculate_knots(const int points_num, const bool cyclic, MutableSpan<float> knots) { - BLI_assert(knots.size() == knots_size(points_num, order, cyclic)); + BLI_assert(knots.size() == knots_num(points_num, order, cyclic)); UNUSED_VARS_NDEBUG(points_num); const bool is_bezier = ELEM(mode, NURBS_KNOT_MODE_BEZIER, NURBS_KNOT_MODE_ENDPOINT_BEZIER); @@ -147,7 +147,7 @@ static void calculate_basis_for_point(const float parameter, } void calculate_basis_cache(const int points_num, - const int evaluated_size, + const int evaluated_num, const int8_t order, const bool cyclic, const Span<float> knots, @@ -157,10 +157,10 @@ void calculate_basis_cache(const int points_num, const int8_t degree = order - 1; - basis_cache.weights.resize(evaluated_size * order); - basis_cache.start_indices.resize(evaluated_size); + basis_cache.weights.resize(evaluated_num * order); + basis_cache.start_indices.resize(evaluated_num); - if (evaluated_size == 0) { + if (evaluated_num == 0) { return; } @@ -168,12 +168,12 @@ void calculate_basis_cache(const int points_num, MutableSpan<int> basis_start_indices(basis_cache.start_indices); const int last_control_point_index = cyclic ? points_num + degree : points_num; - const int evaluated_segment_size = curve_segment_size(evaluated_size, cyclic); + const int evaluated_segment_num = curve_segment_num(evaluated_num, cyclic); const float start = knots[degree]; const float end = knots[last_control_point_index]; - const float step = (end - start) / evaluated_segment_size; - for (const int i : IndexRange(evaluated_size)) { + const float step = (end - start) / evaluated_segment_num; + for (const int i : IndexRange(evaluated_num)) { /* Clamp parameter due to floating point inaccuracy. */ const float parameter = std::clamp(start + step * i, knots[0], knots[points_num + degree]); @@ -232,8 +232,12 @@ void interpolate_to_evaluated(const BasisCache &basis_cache, const GSpan src, GMutableSpan dst) { - BLI_assert(dst.size() == basis_cache.start_indices.size()); + if (basis_cache.invalid) { + dst.copy_from(src); + return; + } + BLI_assert(dst.size() == basis_cache.start_indices.size()); attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { diff --git a/source/blender/blenkernel/intern/curve_poly.cc b/source/blender/blenkernel/intern/curve_poly.cc index 2db7cd71ad3..1b337379604 100644 --- a/source/blender/blenkernel/intern/curve_poly.cc +++ b/source/blender/blenkernel/intern/curve_poly.cc @@ -110,7 +110,7 @@ void calculate_normals_minimum(const Span<float3> tangents, normals.first() = math::normalize(float3(first_tangent.y, -first_tangent.x, 0.0f)); } - /* Forward normal with minimum twist along the entire spline. */ + /* Forward normal with minimum twist along the entire curve. */ for (const int i : IndexRange(1, normals.size() - 1)) { normals[i] = calculate_next_normal(normals[i - 1], tangents[i - 1], tangents[i]); } @@ -120,7 +120,7 @@ void calculate_normals_minimum(const Span<float3> tangents, } /* Compute how much the first normal deviates from the normal that has been forwarded along the - * entire cyclic spline. */ + * entire cyclic curve. */ const float3 uncorrected_last_normal = calculate_next_normal( normals.last(), tangents.last(), tangents.first()); float correction_angle = angle_signed_on_axis_v3v3_v3( diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index ef921797698..0cd324cfe2c 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -39,8 +39,8 @@ static void fill_mesh_topology(const int vert_offset, MutableSpan<MLoop> loops, MutableSpan<MPoly> polys) { - const int main_segment_num = curves::curve_segment_size(main_point_num, main_cyclic); - const int profile_segment_num = curves::curve_segment_size(profile_point_num, profile_cyclic); + const int main_segment_num = curves::curve_segment_num(main_point_num, main_cyclic); + const int profile_segment_num = curves::curve_segment_num(profile_point_num, profile_cyclic); if (profile_point_num == 1) { for (const int i : IndexRange(main_point_num - 1)) { @@ -134,9 +134,9 @@ static void fill_mesh_topology(const int vert_offset, const bool has_caps = fill_caps && !main_cyclic && profile_cyclic; if (has_caps) { - const int poly_size = main_segment_num * profile_segment_num; - const int cap_loop_offset = loop_offset + poly_size * 4; - const int cap_poly_offset = poly_offset + poly_size; + const int poly_num = main_segment_num * profile_segment_num; + const int cap_loop_offset = loop_offset + poly_num * 4; + const int cap_poly_offset = poly_offset + poly_num; MPoly &poly_start = polys[cap_poly_offset]; poly_start.loopstart = cap_loop_offset; @@ -273,7 +273,7 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool for (const int i_main : info.main.curves_range()) { const bool main_cyclic = info.main_cyclic[i_main]; const int main_point_num = info.main.evaluated_points_for_curve(i_main).size(); - const int main_segment_num = curves::curve_segment_size(main_point_num, main_cyclic); + const int main_segment_num = curves::curve_segment_num(main_point_num, main_cyclic); for (const int i_profile : info.profile.curves_range()) { result.vert[mesh_index] = vert_offset; result.edge[mesh_index] = edge_offset; @@ -285,8 +285,7 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool const bool profile_cyclic = info.profile_cyclic[i_profile]; const int profile_point_num = info.profile.evaluated_points_for_curve(i_profile).size(); - const int profile_segment_num = curves::curve_segment_size(profile_point_num, - profile_cyclic); + const int profile_segment_num = curves::curve_segment_num(profile_point_num, profile_cyclic); const bool has_caps = fill_caps && !main_cyclic && profile_cyclic; const int tube_face_num = main_segment_num * profile_segment_num; @@ -408,8 +407,8 @@ static void foreach_curve_combination(const CurvesInfo &info, profile_points, main_cyclic, profile_cyclic, - curves::curve_segment_size(main_points.size(), main_cyclic), - curves::curve_segment_size(profile_points.size(), profile_cyclic), + curves::curve_segment_num(main_points.size(), main_cyclic), + curves::curve_segment_num(profile_points.size(), profile_cyclic), offsets_to_range(offsets.vert.as_span(), i), offsets_to_range(offsets.edge.as_span(), i), offsets_to_range(offsets.poly.as_span(), i), diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index 1df1492bac1..d38bc790978 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -78,12 +78,12 @@ static void curves_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, * shallow copy from the source to the destination, and because the copy-on-write functionality * isn't supported more generically yet. */ - dst.point_size = src.point_size; - dst.curve_size = src.curve_size; + dst.point_num = src.point_num; + dst.curve_num = src.curve_num; const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE; - CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, alloc_type, dst.point_size); - CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, alloc_type, dst.curve_size); + CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, alloc_type, dst.point_num); + CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, alloc_type, dst.curve_num); dst.curve_offsets = static_cast<int *>(MEM_dupallocN(src.curve_offsets)); @@ -136,17 +136,17 @@ static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_addre CustomData_blend_write(writer, &curves->geometry.point_data, players, - curves->geometry.point_size, + curves->geometry.point_num, CD_MASK_ALL, &curves->id); CustomData_blend_write(writer, &curves->geometry.curve_data, clayers, - curves->geometry.curve_size, + curves->geometry.curve_num, CD_MASK_ALL, &curves->id); - BLO_write_int32_array(writer, curves->geometry.curve_size + 1, curves->geometry.curve_offsets); + BLO_write_int32_array(writer, curves->geometry.curve_num + 1, curves->geometry.curve_offsets); BLO_write_pointer_array(writer, curves->totcol, curves->mat); if (curves->adt) { @@ -169,11 +169,11 @@ static void curves_blend_read_data(BlendDataReader *reader, ID *id) BKE_animdata_blend_read_data(reader, curves->adt); /* Geometry */ - CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_size); - CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_size); + CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_num); + CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_num); update_custom_data_pointers(*curves); - BLO_read_int32_array(reader, curves->geometry.curve_size + 1, &curves->geometry.curve_offsets); + BLO_read_int32_array(reader, curves->geometry.curve_num + 1, &curves->geometry.curve_offsets); curves->geometry.runtime = MEM_new<blender::bke::CurvesGeometryRuntime>(__func__); @@ -247,7 +247,7 @@ void *BKE_curves_add(Main *bmain, const char *name) BoundBox *BKE_curves_boundbox_get(Object *ob) { BLI_assert(ob->type == OB_CURVES); - Curves *curves = static_cast<Curves *>(ob->data); + const Curves *curves_id = static_cast<const Curves *>(ob->data); if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) { return ob->runtime.bb; @@ -256,11 +256,12 @@ BoundBox *BKE_curves_boundbox_get(Object *ob) if (ob->runtime.bb == nullptr) { ob->runtime.bb = MEM_cnew<BoundBox>(__func__); - blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(curves->geometry); + const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( + curves_id->geometry); float3 min(FLT_MAX); float3 max(-FLT_MAX); - if (!geometry.bounds_min_max(min, max)) { + if (!curves.bounds_min_max(min, max)) { min = float3(-1); max = float3(1); } @@ -364,19 +365,26 @@ namespace blender::bke { Curves *curves_new_nomain(const int points_num, const int curves_num) { - Curves *curves = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr)); - CurvesGeometry &geometry = CurvesGeometry::wrap(curves->geometry); - geometry.resize(points_num, curves_num); - return curves; + Curves *curves_id = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr)); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); + curves.resize(points_num, curves_num); + return curves_id; } Curves *curves_new_nomain_single(const int points_num, const CurveType type) { - Curves *curves = curves_new_nomain(points_num, 1); - CurvesGeometry &geometry = CurvesGeometry::wrap(curves->geometry); - geometry.offsets_for_write().last() = points_num; - geometry.fill_curve_types(type); - return curves; + Curves *curves_id = curves_new_nomain(points_num, 1); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); + curves.offsets_for_write().last() = points_num; + curves.fill_curve_types(type); + return curves_id; +} + +Curves *curves_new_nomain(CurvesGeometry curves) +{ + Curves *curves_id = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr)); + bke::CurvesGeometry::wrap(curves_id->geometry) = std::move(curves); + return curves_id; } } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 7a09b87490b..0fd58a52f81 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -46,10 +46,10 @@ CurvesGeometry::CurvesGeometry() : CurvesGeometry(0, 0) { } -CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size) +CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num) { - this->point_size = point_size; - this->curve_size = curve_size; + this->point_num = point_num; + this->curve_num = curve_num; CustomData_reset(&this->point_data); CustomData_reset(&this->curve_data); @@ -57,14 +57,16 @@ CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size) CD_PROP_FLOAT3, CD_DEFAULT, nullptr, - this->point_size, + this->point_num, ATTR_POSITION.c_str()); - this->curve_offsets = (int *)MEM_calloc_arrayN(this->curve_size + 1, sizeof(int), __func__); + this->curve_offsets = (int *)MEM_calloc_arrayN(this->curve_num + 1, sizeof(int), __func__); this->update_customdata_pointers(); this->runtime = MEM_new<CurvesGeometryRuntime>(__func__); + /* Fill the type counts with the default so they're in a valid state. */ + this->runtime->type_counts[CURVE_TYPE_CATMULL_ROM] = curve_num; } /** @@ -72,15 +74,15 @@ CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size) */ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src) { - CustomData_free(&dst.point_data, dst.point_size); - CustomData_free(&dst.curve_data, dst.curve_size); - dst.point_size = src.point_size; - dst.curve_size = src.curve_size; - CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, CD_DUPLICATE, dst.point_size); - CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, CD_DUPLICATE, dst.curve_size); + CustomData_free(&dst.point_data, dst.point_num); + CustomData_free(&dst.curve_data, dst.curve_num); + dst.point_num = src.point_num; + dst.curve_num = src.curve_num; + CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, CD_DUPLICATE, dst.point_num); + CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, CD_DUPLICATE, dst.curve_num); MEM_SAFE_FREE(dst.curve_offsets); - dst.curve_offsets = (int *)MEM_calloc_arrayN(dst.point_size + 1, sizeof(int), __func__); + dst.curve_offsets = (int *)MEM_calloc_arrayN(dst.point_num + 1, sizeof(int), __func__); dst.offsets_for_write().copy_from(src.offsets()); dst.tag_topology_changed(); @@ -92,7 +94,7 @@ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src) } CurvesGeometry::CurvesGeometry(const CurvesGeometry &other) - : CurvesGeometry(other.point_size, other.curve_size) + : CurvesGeometry(other.point_num, other.curve_num) { copy_curves_geometry(*this, other); } @@ -108,15 +110,15 @@ CurvesGeometry &CurvesGeometry::operator=(const CurvesGeometry &other) /* The source should be empty, but in a valid state so that using it further will work. */ static void move_curves_geometry(CurvesGeometry &dst, CurvesGeometry &src) { - dst.point_size = src.point_size; + dst.point_num = src.point_num; std::swap(dst.point_data, src.point_data); - CustomData_free(&src.point_data, src.point_size); - src.point_size = 0; + CustomData_free(&src.point_data, src.point_num); + src.point_num = 0; - dst.curve_size = src.curve_size; + dst.curve_num = src.curve_num; std::swap(dst.curve_data, src.curve_data); - CustomData_free(&src.curve_data, src.curve_size); - src.curve_size = 0; + CustomData_free(&src.curve_data, src.curve_num); + src.curve_num = 0; std::swap(dst.curve_offsets, src.curve_offsets); MEM_SAFE_FREE(src.curve_offsets); @@ -128,7 +130,7 @@ static void move_curves_geometry(CurvesGeometry &dst, CurvesGeometry &src) } CurvesGeometry::CurvesGeometry(CurvesGeometry &&other) - : CurvesGeometry(other.point_size, other.curve_size) + : CurvesGeometry(other.point_num, other.curve_num) { move_curves_geometry(*this, other); } @@ -143,8 +145,8 @@ CurvesGeometry &CurvesGeometry::operator=(CurvesGeometry &&other) CurvesGeometry::~CurvesGeometry() { - CustomData_free(&this->point_data, this->point_size); - CustomData_free(&this->curve_data, this->curve_size); + CustomData_free(&this->point_data, this->point_num); + CustomData_free(&this->curve_data, this->curve_num); MEM_SAFE_FREE(this->curve_offsets); MEM_delete(this->runtime); this->runtime = nullptr; @@ -156,7 +158,7 @@ CurvesGeometry::~CurvesGeometry() /** \name Accessors * \{ */ -static int domain_size(const CurvesGeometry &curves, const AttributeDomain domain) +static int domain_num(const CurvesGeometry &curves, const AttributeDomain domain) { return domain == ATTR_DOMAIN_POINT ? curves.points_num() : curves.curves_num(); } @@ -178,15 +180,15 @@ static VArray<T> get_varray_attribute(const CurvesGeometry &curves, const StringRefNull name, const T default_value) { - const int size = domain_size(curves, domain); + const int num = domain_num(curves, domain); const CustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>()); const CustomData &custom_data = domain_custom_data(curves, domain); const T *data = (const T *)CustomData_get_layer_named(&custom_data, type, name.c_str()); if (data != nullptr) { - return VArray<T>::ForSpan(Span<T>(data, size)); + return VArray<T>::ForSpan(Span<T>(data, num)); } - return VArray<T>::ForSingle(default_value, size); + return VArray<T>::ForSingle(default_value, num); } template<typename T> @@ -194,7 +196,7 @@ static Span<T> get_span_attribute(const CurvesGeometry &curves, const AttributeDomain domain, const StringRefNull name) { - const int size = domain_size(curves, domain); + const int num = domain_num(curves, domain); const CustomData &custom_data = domain_custom_data(curves, domain); const CustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>()); @@ -202,7 +204,7 @@ static Span<T> get_span_attribute(const CurvesGeometry &curves, if (data == nullptr) { return {}; } - return {data, size}; + return {data, num}; } template<typename T> @@ -211,19 +213,19 @@ static MutableSpan<T> get_mutable_attribute(CurvesGeometry &curves, const StringRefNull name, const T default_value = T()) { - const int size = domain_size(curves, domain); + const int num = domain_num(curves, domain); const CustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>()); CustomData &custom_data = domain_custom_data(curves, domain); T *data = (T *)CustomData_duplicate_referenced_layer_named( - &custom_data, type, name.c_str(), size); + &custom_data, type, name.c_str(), num); if (data != nullptr) { - return {data, size}; + return {data, num}; } data = (T *)CustomData_add_layer_named( - &custom_data, type, CD_CALLOC, nullptr, size, name.c_str()); - MutableSpan<T> span = {data, size}; - if (size > 0 && span.first() != default_value) { + &custom_data, type, CD_CALLOC, nullptr, num, name.c_str()); + MutableSpan<T> span = {data, num}; + if (num > 0 && span.first() != default_value) { span.fill(default_value); } return span; @@ -250,6 +252,10 @@ void CurvesGeometry::fill_curve_types(const CurveType type) void CurvesGeometry::fill_curve_types(const IndexMask selection, const CurveType type) { + if (selection.size() == this->curves_num()) { + this->fill_curve_types(type); + return; + } /* A potential performance optimization is only counting the changed indices. */ this->curve_types_for_write().fill_indices(selection, type); this->update_curve_types(); @@ -295,22 +301,22 @@ void CurvesGeometry::update_curve_types() Span<float3> CurvesGeometry::positions() const { - return {(const float3 *)this->position, this->point_size}; + return {(const float3 *)this->position, this->point_num}; } MutableSpan<float3> CurvesGeometry::positions_for_write() { this->position = (float(*)[3])CustomData_duplicate_referenced_layer_named( - &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str(), this->point_size); - return {(float3 *)this->position, this->point_size}; + &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str(), this->point_num); + return {(float3 *)this->position, this->point_num}; } Span<int> CurvesGeometry::offsets() const { - return {this->curve_offsets, this->curve_size + 1}; + return {this->curve_offsets, this->curve_num + 1}; } MutableSpan<int> CurvesGeometry::offsets_for_write() { - return {this->curve_offsets, this->curve_size + 1}; + return {this->curve_offsets, this->curve_num + 1}; } VArray<bool> CurvesGeometry::cyclic() const @@ -438,12 +444,12 @@ MutableSpan<float2> CurvesGeometry::surface_triangle_coords_for_write() /** \name Evaluation * \{ */ -template<typename SizeFn> void build_offsets(MutableSpan<int> offsets, const SizeFn &size_fn) +template<typename CountFn> void build_offsets(MutableSpan<int> offsets, const CountFn &count_fn) { int offset = 0; for (const int i : offsets.drop_back(1).index_range()) { offsets[i] = offset; - offset += size_fn(i); + offset += count_fn(i); } offsets.last() = offset; } @@ -466,7 +472,7 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves, const IndexRange points = curves.points_for_curve(curve_index); switch (types[curve_index]) { case CURVE_TYPE_CATMULL_ROM: - return curves::catmull_rom::calculate_evaluated_size( + return curves::catmull_rom::calculate_evaluated_num( points.size(), cyclic[curve_index], resolution[curve_index]); case CURVE_TYPE_POLY: return points.size(); @@ -478,11 +484,11 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves, bezier_evaluated_offsets.slice(points)); return bezier_evaluated_offsets[points.last()]; case CURVE_TYPE_NURBS: - return curves::nurbs::calculate_evaluated_size(points.size(), - nurbs_orders[curve_index], - cyclic[curve_index], - resolution[curve_index], - KnotsMode(nurbs_knots_modes[curve_index])); + return curves::nurbs::calculate_evaluated_num(points.size(), + nurbs_orders[curve_index], + cyclic[curve_index], + resolution[curve_index], + KnotsMode(nurbs_knots_modes[curve_index])); } BLI_assert_unreachable(); return 0; @@ -527,19 +533,23 @@ Span<int> CurvesGeometry::evaluated_offsets() const IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type, Vector<int64_t> &r_indices) const { + return this->indices_for_curve_type(type, this->curves_range(), r_indices); +} - VArray<int8_t> types = this->curve_types(); +IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type, + const IndexMask selection, + Vector<int64_t> &r_indices) const +{ + if (this->curve_type_counts()[type] == this->curves_num()) { + return selection; + } + const VArray<int8_t> types = this->curve_types(); if (types.is_single()) { - if (types.get_internal_single() == type) { - return IndexMask(types.size()); - } - return {}; + return types.get_internal_single() == type ? IndexMask(this->curves_num()) : IndexMask(0); } Span<int8_t> types_span = types.get_internal_span(); return index_mask_ops::find_indices_based_on_predicate( - IndexMask(types.size()), 1024, r_indices, [&](const int index) { - return types_span[index] == type; - }); + selection, 1024, r_indices, [&](const int index) { return types_span[index] == type; }); } void CurvesGeometry::ensure_nurbs_basis_cache() const @@ -577,8 +587,13 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const const bool is_cyclic = cyclic[curve_index]; const KnotsMode mode = KnotsMode(knots_modes[curve_index]); - const int knots_size = curves::nurbs::knots_size(points.size(), order, is_cyclic); - Array<float> knots(knots_size); + if (!curves::nurbs::check_valid_num_and_order(points.size(), order, is_cyclic, mode)) { + basis_caches[curve_index].invalid = true; + continue; + } + + const int knots_num = curves::nurbs::knots_num(points.size(), order, is_cyclic); + Array<float> knots(knots_num); curves::nurbs::calculate_knots(points.size(), mode, order, is_cyclic, knots); curves::nurbs::calculate_basis_cache(points.size(), evaluated_points.size(), @@ -696,9 +711,6 @@ Span<float3> CurvesGeometry::evaluated_tangents() const threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) { for (const int curve_index : curves_range) { const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); - if (UNLIKELY(evaluated_points.is_empty())) { - continue; - } curves::poly::calculate_tangents(evaluated_positions.slice(evaluated_points), cyclic[curve_index], tangents.slice(evaluated_points)); @@ -773,9 +785,6 @@ Span<float3> CurvesGeometry::evaluated_normals() const for (const int curve_index : curves_range) { const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); - if (UNLIKELY(evaluated_points.is_empty())) { - continue; - } switch (normal_mode[curve_index]) { case NORMAL_MODE_Z_UP: curves::poly::calculate_normals_z_up(evaluated_tangents.slice(evaluated_points), @@ -905,8 +914,8 @@ void CurvesGeometry::ensure_evaluated_lengths() const threading::isolate_task([&]() { /* Use an extra length value for the final cyclic segment for a consistent size * (see comment on #evaluated_length_cache). */ - const int total_size = this->evaluated_points_num() + this->curves_num(); - this->runtime->evaluated_length_cache.resize(total_size); + const int total_num = this->evaluated_points_num() + this->curves_num(); + this->runtime->evaluated_length_cache.resize(total_num); MutableSpan<float> evaluated_lengths = this->runtime->evaluated_length_cache; Span<float3> evaluated_positions = this->evaluated_positions(); @@ -916,9 +925,6 @@ void CurvesGeometry::ensure_evaluated_lengths() const for (const int curve_index : curves_range) { const bool cyclic = curves_cyclic[curve_index]; const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); - if (UNLIKELY(evaluated_points.is_empty())) { - continue; - } const IndexRange lengths_range = this->lengths_range_for_curve(curve_index, cyclic); length_parameterize::accumulate_lengths(evaluated_positions.slice(evaluated_points), cyclic, @@ -938,13 +944,13 @@ void CurvesGeometry::ensure_evaluated_lengths() const void CurvesGeometry::resize(const int points_num, const int curves_num) { - if (points_num != this->point_size) { + if (points_num != this->point_num) { CustomData_realloc(&this->point_data, points_num); - this->point_size = points_num; + this->point_num = points_num; } - if (curves_num != this->curve_size) { + if (curves_num != this->curve_num) { CustomData_realloc(&this->curve_data, curves_num); - this->curve_size = curves_num; + this->curve_num = curves_num; this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curves_num + 1)); } this->tag_topology_changed(); @@ -1196,6 +1202,8 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, } }); + new_curves.update_curve_types(); + return new_curves; } diff --git a/source/blender/blenkernel/intern/curves_utils.cc b/source/blender/blenkernel/intern/curves_utils.cc new file mode 100644 index 00000000000..78c2382b62f --- /dev/null +++ b/source/blender/blenkernel/intern/curves_utils.cc @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "BKE_curves_utils.hh" + +namespace blender::bke::curves { + +void fill_curve_counts(const bke::CurvesGeometry &curves, + const Span<IndexRange> curve_ranges, + MutableSpan<int> counts) +{ + threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange ranges_range) { + for (const IndexRange curves_range : curve_ranges.slice(ranges_range)) { + threading::parallel_for(curves_range, 4096, [&](IndexRange range) { + for (const int i : range) { + counts[i] = curves.points_for_curve(i).size(); + } + }); + } + }); +} + +void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, const int start_offset) +{ + int offset = start_offset; + for (const int i : counts_to_offsets.index_range().drop_back(1)) { + const int count = counts_to_offsets[i]; + BLI_assert(count > 0); + counts_to_offsets[i] = offset; + offset += count; + } + counts_to_offsets.last() = offset; +} + +} // namespace blender::bke::curves diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 62351a31042..623c3bb698c 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -2575,18 +2575,24 @@ void CustomData_set_layer_stencil(CustomData *data, int type, int n) void CustomData_set_layer_active_index(CustomData *data, int type, int n) { + const int layer_index = data->typemap[type]; + BLI_assert(customdata_typemap_is_valid(data)); + for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { - data->layers[i].active = n - i; + data->layers[i].active = n - layer_index; } } } void CustomData_set_layer_render_index(CustomData *data, int type, int n) { + const int layer_index = data->typemap[type]; + BLI_assert(customdata_typemap_is_valid(data)); + for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { - data->layers[i].active_rnd = n - i; + data->layers[i].active_rnd = n - layer_index; } } } @@ -3646,7 +3652,7 @@ void CustomData_bmesh_init_pool(CustomData *data, int totelem, const char htype) chunksize = bm_mesh_chunksize_default.totface; break; default: - BLI_assert(0); + BLI_assert_unreachable(); chunksize = 512; break; } diff --git a/source/blender/blenkernel/intern/customdata_file.c b/source/blender/blenkernel/intern/customdata_file.c index 9716b4909b2..cbfaf2831d1 100644 --- a/source/blender/blenkernel/intern/customdata_file.c +++ b/source/blender/blenkernel/intern/customdata_file.c @@ -268,11 +268,11 @@ static bool cdf_write_header(CDataFile *cdf) return true; } -bool cdf_read_open(CDataFile *cdf, const char *filename) +bool cdf_read_open(CDataFile *cdf, const char *filepath) { FILE *f; - f = BLI_fopen(filename, "rb"); + f = BLI_fopen(filepath, "rb"); if (!f) { return false; } @@ -333,14 +333,14 @@ void cdf_read_close(CDataFile *cdf) } } -bool cdf_write_open(CDataFile *cdf, const char *filename) +bool cdf_write_open(CDataFile *cdf, const char *filepath) { CDataFileHeader *header; CDataFileImageHeader *image; CDataFileMeshHeader *mesh; FILE *f; - f = BLI_fopen(filename, "wb"); + f = BLI_fopen(filepath, "wb"); if (!f) { return false; } @@ -402,9 +402,9 @@ void cdf_write_close(CDataFile *cdf) } } -void cdf_remove(const char *filename) +void cdf_remove(const char *filepath) { - BLI_delete(filename, false, false); + BLI_delete(filepath, false, false); } /********************************** Layers ***********************************/ diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 6a3f6a47f5e..196a6a00ade 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -222,7 +222,7 @@ int BKE_object_data_transfer_dttype_to_cdtype(const int dtdata_type) case DT_TYPE_MPROPCOL_LOOP: return CD_PROP_COLOR; default: - BLI_assert(0); + BLI_assert_unreachable(); } return 0; /* Should never be reached! */ } @@ -477,7 +477,7 @@ static void data_transfer_layersmapping_add_item_cd(ListBase *r_map, const int mix_mode, const float mix_factor, const float *mix_weights, - void *data_src, + const void *data_src, void *data_dst, cd_datatransfer_interp interp, void *interp_data) @@ -532,7 +532,8 @@ static bool data_transfer_layersmapping_cdlayers_multisrc_to_dst(ListBase *r_map cd_datatransfer_interp interp, void *interp_data) { - void *data_src, *data_dst = NULL; + const void *data_src; + void *data_dst = NULL; int idx_src = num_layers_src; int idx_dst, tot_dst = CustomData_number_of_layers(cd_dst, cddata_type); bool *data_dst_to_delete = NULL; @@ -695,7 +696,8 @@ static bool data_transfer_layersmapping_cdlayers(ListBase *r_map, void *interp_data) { int idx_src, idx_dst; - void *data_src, *data_dst = NULL; + const void *data_src; + void *data_dst = NULL; if (CustomData_layertype_is_singleton(cddata_type)) { if (!(data_src = CustomData_get_layer(cd_src, cddata_type))) { @@ -1369,7 +1371,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, /* Assumed always true if not using an evaluated mesh as destination. */ bool dirty_nors_dst = true; - MDeformVert *mdef = NULL; + const MDeformVert *mdef = NULL; int vg_idx = -1; float *weights[DATAMAX] = {NULL}; @@ -1509,7 +1511,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, if (mdef && vg_idx != -1 && !weights[VDATA]) { weights[VDATA] = MEM_mallocN(sizeof(*(weights[VDATA])) * (size_t)num_verts_dst, __func__); BKE_defvert_extract_vgroup_to_vertweights( - mdef, vg_idx, num_verts_dst, weights[VDATA], invert_vgroup); + mdef, vg_idx, num_verts_dst, invert_vgroup, weights[VDATA]); } if (data_transfer_layersmapping_generate(&lay_map, @@ -1588,7 +1590,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, if (mdef && vg_idx != -1 && !weights[EDATA]) { weights[EDATA] = MEM_mallocN(sizeof(*weights[EDATA]) * (size_t)num_edges_dst, __func__); BKE_defvert_extract_vgroup_to_edgeweights( - mdef, vg_idx, num_verts_dst, edges_dst, num_edges_dst, weights[EDATA], invert_vgroup); + mdef, vg_idx, num_verts_dst, edges_dst, num_edges_dst, invert_vgroup, weights[EDATA]); } if (data_transfer_layersmapping_generate(&lay_map, @@ -1683,7 +1685,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, if (mdef && vg_idx != -1 && !weights[LDATA]) { weights[LDATA] = MEM_mallocN(sizeof(*weights[LDATA]) * (size_t)num_loops_dst, __func__); BKE_defvert_extract_vgroup_to_loopweights( - mdef, vg_idx, num_verts_dst, loops_dst, num_loops_dst, weights[LDATA], invert_vgroup); + mdef, vg_idx, num_verts_dst, loops_dst, num_loops_dst, invert_vgroup, weights[LDATA]); } if (data_transfer_layersmapping_generate(&lay_map, @@ -1769,8 +1771,8 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, num_loops_dst, polys_dst, num_polys_dst, - weights[PDATA], - invert_vgroup); + invert_vgroup, + weights[PDATA]); } if (data_transfer_layersmapping_generate(&lay_map, diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index 1d276e9108c..c7ac82e4b68 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -147,9 +147,9 @@ void BKE_defvert_copy_index(MDeformVert *dvert_dst, const MDeformVert *dvert_src, const int defgroup_src) { - MDeformWeight *dw_src, *dw_dst; + MDeformWeight *dw_dst; - dw_src = BKE_defvert_find_index(dvert_src, defgroup_src); + const MDeformWeight *dw_src = BKE_defvert_find_index(dvert_src, defgroup_src); if (dw_src) { /* Source is valid, ensure destination is created. */ @@ -1008,11 +1008,11 @@ void BKE_defvert_array_free(MDeformVert *dvert, int totvert) MEM_freeN(dvert); } -void BKE_defvert_extract_vgroup_to_vertweights(MDeformVert *dvert, +void BKE_defvert_extract_vgroup_to_vertweights(const MDeformVert *dvert, const int defgroup, const int num_verts, - float *r_weights, - const bool invert_vgroup) + const bool invert_vgroup, + float *r_weights) { if (dvert && defgroup != -1) { int i = num_verts; @@ -1027,20 +1027,20 @@ void BKE_defvert_extract_vgroup_to_vertweights(MDeformVert *dvert, } } -void BKE_defvert_extract_vgroup_to_edgeweights(MDeformVert *dvert, +void BKE_defvert_extract_vgroup_to_edgeweights(const MDeformVert *dvert, const int defgroup, const int num_verts, MEdge *edges, const int num_edges, - float *r_weights, - const bool invert_vgroup) + const bool invert_vgroup, + float *r_weights) { if (dvert && defgroup != -1) { int i = num_edges; float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__); BKE_defvert_extract_vgroup_to_vertweights( - dvert, defgroup, num_verts, tmp_weights, invert_vgroup); + dvert, defgroup, num_verts, invert_vgroup, tmp_weights); while (i--) { MEdge *me = &edges[i]; @@ -1055,20 +1055,20 @@ void BKE_defvert_extract_vgroup_to_edgeweights(MDeformVert *dvert, } } -void BKE_defvert_extract_vgroup_to_loopweights(MDeformVert *dvert, +void BKE_defvert_extract_vgroup_to_loopweights(const MDeformVert *dvert, const int defgroup, const int num_verts, MLoop *loops, const int num_loops, - float *r_weights, - const bool invert_vgroup) + const bool invert_vgroup, + float *r_weights) { if (dvert && defgroup != -1) { int i = num_loops; float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__); BKE_defvert_extract_vgroup_to_vertweights( - dvert, defgroup, num_verts, tmp_weights, invert_vgroup); + dvert, defgroup, num_verts, invert_vgroup, tmp_weights); while (i--) { MLoop *ml = &loops[i]; @@ -1083,22 +1083,22 @@ void BKE_defvert_extract_vgroup_to_loopweights(MDeformVert *dvert, } } -void BKE_defvert_extract_vgroup_to_polyweights(MDeformVert *dvert, +void BKE_defvert_extract_vgroup_to_polyweights(const MDeformVert *dvert, const int defgroup, const int num_verts, MLoop *loops, const int UNUSED(num_loops), MPoly *polys, const int num_polys, - float *r_weights, - const bool invert_vgroup) + const bool invert_vgroup, + float *r_weights) { if (dvert && defgroup != -1) { int i = num_polys; float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__); BKE_defvert_extract_vgroup_to_vertweights( - dvert, defgroup, num_verts, tmp_weights, invert_vgroup); + dvert, defgroup, num_verts, invert_vgroup, tmp_weights); while (i--) { MPoly *mp = &polys[i]; @@ -1193,7 +1193,7 @@ static bool data_transfer_layersmapping_vgroups_multisrc_to_dst(ListBase *r_map, const bool use_delete, Object *ob_src, Object *ob_dst, - MDeformVert *data_src, + const MDeformVert *data_src, MDeformVert *data_dst, CustomData *UNUSED(cd_src), CustomData *cd_dst, @@ -1348,7 +1348,6 @@ bool data_transfer_layersmapping_vgroups(ListBase *r_map, const int tolayers) { int idx_src, idx_dst; - MDeformVert *data_src, *data_dst = NULL; const size_t elem_size = sizeof(*((MDeformVert *)NULL)); @@ -1370,9 +1369,9 @@ bool data_transfer_layersmapping_vgroups(ListBase *r_map, return true; } - data_src = CustomData_get_layer(cd_src, CD_MDEFORMVERT); + const MDeformVert *data_src = CustomData_get_layer(cd_src, CD_MDEFORMVERT); - data_dst = CustomData_get_layer(cd_dst, CD_MDEFORMVERT); + MDeformVert *data_dst = CustomData_get_layer(cd_dst, CD_MDEFORMVERT); if (data_dst && use_dupref_dst && r_map) { /* If dest is a derivedmesh, we do not want to overwrite cdlayers of org mesh! */ data_dst = CustomData_duplicate_referenced_layer(cd_dst, CD_MDEFORMVERT, num_elem_dst); diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 22341f98375..48f2d66c1cd 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -1607,7 +1607,6 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface const MLoop *mloop = mesh->mloop; const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(mesh); const int tottri = BKE_mesh_runtime_looptri_len(mesh); - const MLoopUV *mloopuv = NULL; char uvname[MAX_CUSTOMDATA_LAYER_NAME]; @@ -1617,7 +1616,7 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface /* get uv map */ CustomData_validate_layer_name(&mesh->ldata, CD_MLOOPUV, surface->init_layername, uvname); - mloopuv = CustomData_get_layer_named(&mesh->ldata, CD_MLOOPUV, uvname); + const MLoopUV *mloopuv = CustomData_get_layer_named(&mesh->ldata, CD_MLOOPUV, uvname); if (!mloopuv) { return; } @@ -1675,7 +1674,7 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface } else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(mesh); - MLoopCol *col = CustomData_get_layer_named( + const MLoopCol *col = CustomData_get_layer_named( &mesh->ldata, CD_PROP_BYTE_COLOR, surface->init_layername); if (!col) { return; diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index d176bf41254..a952da6fa52 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -15,6 +15,7 @@ #include "BLI_math.h" #include "BKE_DerivedMesh.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_editmesh_cache.h" #include "BKE_lib_id.h" diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 5de13fbdbed..06d32d5bfd4 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -1824,7 +1824,7 @@ static void sample_mesh(FluidFlowSettings *ffs, const float *vert_vel, bool has_velocity, int defgrp_index, - MDeformVert *dvert, + const MDeformVert *dvert, float x, float y, float z) @@ -2008,7 +2008,7 @@ typedef struct EmitFromDMData { const MLoop *mloop; const MLoopTri *mlooptri; const MLoopUV *mloopuv; - MDeformVert *dvert; + const MDeformVert *dvert; int defgrp_index; BVHTreeFromMesh *tree; @@ -2074,14 +2074,8 @@ static void emit_from_mesh( Object *flow_ob, FluidDomainSettings *fds, FluidFlowSettings *ffs, FluidObjectBB *bb, float dt) { if (ffs->mesh) { - Mesh *me = NULL; - MVert *mvert = NULL; - const MLoopTri *mlooptri = NULL; - const MLoop *mloop = NULL; - const MLoopUV *mloopuv = NULL; - MDeformVert *dvert = NULL; BVHTreeFromMesh tree_data = {NULL}; - int numverts, i; + int i; float *vert_vel = NULL; bool has_velocity = false; @@ -2092,7 +2086,7 @@ static void emit_from_mesh( /* Copy mesh for thread safety as we modify it. * Main issue is its VertArray being modified, then replaced and freed. */ - me = BKE_mesh_copy_for_eval(ffs->mesh, true); + Mesh *me = BKE_mesh_copy_for_eval(ffs->mesh, true); /* Duplicate vertices to modify. */ if (me->mvert) { @@ -2100,12 +2094,12 @@ static void emit_from_mesh( CustomData_set_layer(&me->vdata, CD_MVERT, me->mvert); } - mvert = me->mvert; - mloop = me->mloop; - mlooptri = BKE_mesh_runtime_looptri_ensure(me); - numverts = me->totvert; - dvert = CustomData_get_layer(&me->vdata, CD_MDEFORMVERT); - mloopuv = CustomData_get_layer_named(&me->ldata, CD_MLOOPUV, ffs->uvlayer_name); + MVert *mvert = me->mvert; + const MLoop *mloop = me->mloop; + const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(me); + const int numverts = me->totvert; + const MDeformVert *dvert = CustomData_get_layer(&me->vdata, CD_MDEFORMVERT); + const MLoopUV *mloopuv = CustomData_get_layer_named(&me->ldata, CD_MLOOPUV, ffs->uvlayer_name); if (ffs->flags & FLUID_FLOW_INITVELOCITY) { vert_vel = MEM_callocN(sizeof(float[3]) * numverts, "manta_flow_velocity"); diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index f409389e463..a28afc8ddca 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -114,7 +114,7 @@ void CurveComponentLegacy::ensure_owns_direct_data() /** \name Attribute Access Helper Functions * \{ */ -int CurveComponentLegacy::attribute_domain_size(const AttributeDomain domain) const +int CurveComponentLegacy::attribute_domain_num(const AttributeDomain domain) const { if (curve_ == nullptr) { return 0; @@ -251,8 +251,8 @@ template<typename T> class VArray_For_SplineToPoint final : public VArrayImpl<T> void materialize(const IndexMask mask, MutableSpan<T> r_span) const final { - const int total_size = offsets_.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + const int total_num = offsets_.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { for (const int spline_index : original_data_.index_range()) { const int offset = offsets_[spline_index]; const int next_offset = offsets_[spline_index + 1]; @@ -273,8 +273,8 @@ template<typename T> class VArray_For_SplineToPoint final : public VArrayImpl<T> void materialize_to_uninitialized(const IndexMask mask, MutableSpan<T> r_span) const final { T *dst = r_span.data(); - const int total_size = offsets_.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + const int total_num = offsets_.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { for (const int spline_index : original_data_.index_range()) { const int offset = offsets_[spline_index]; const int next_offset = offsets_[spline_index + 1]; @@ -415,7 +415,7 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider { bool exists(const GeometryComponent &component) const final { - return component.attribute_domain_size(ATTR_DOMAIN_CURVE) != 0; + return component.attribute_domain_num(ATTR_DOMAIN_CURVE) != 0; } }; @@ -495,8 +495,8 @@ static void point_attribute_materialize(Span<Span<T>> data, const IndexMask mask, MutableSpan<T> r_span) { - const int total_size = offsets.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + const int total_num = offsets.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { for (const int spline_index : data.index_range()) { const int offset = offsets[spline_index]; const int next_offset = offsets[spline_index + 1]; @@ -541,8 +541,8 @@ static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data, MutableSpan<T> r_span) { T *dst = r_span.data(); - const int total_size = offsets.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + const int total_num = offsets.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { for (const int spline_index : data.index_range()) { const int offset = offsets[spline_index]; const int next_offset = offsets[spline_index + 1]; @@ -589,13 +589,13 @@ static GVArray varray_from_initializer(const AttributeInit &initializer, case AttributeInit::Type::VArray: return static_cast<const AttributeInitVArray &>(initializer).varray; case AttributeInit::Type::MoveArray: - int total_size = 0; + int total_num = 0; for (const SplinePtr &spline : splines) { - total_size += spline->size(); + total_num += spline->size(); } return GVArray::ForSpan(GSpan(*bke::custom_data_type_to_cpp_type(data_type), static_cast<const AttributeInitMove &>(initializer).data, - total_size)); + total_num)); } BLI_assert_unreachable(); return {}; @@ -1168,7 +1168,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { } return curve->has_spline_with_type(CURVE_TYPE_BEZIER) && - component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0; + component.attribute_domain_num(ATTR_DOMAIN_POINT) != 0; } }; diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc index bc9bba3ee2f..d23918215ba 100644 --- a/source/blender/blenkernel/intern/geometry_component_curves.cc +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -237,6 +237,69 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const Attri return nullptr; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Curve Length Field Input + * \{ */ + +static VArray<float> construct_curve_length_gvarray(const CurveComponent &component, + const AttributeDomain domain) +{ + if (!component.has_curves()) { + return {}; + } + const Curves &curves_id = *component.get_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + + curves.ensure_evaluated_lengths(); + + VArray<bool> cyclic = curves.cyclic(); + VArray<float> lengths = VArray<float>::ForFunc( + curves.curves_num(), [&curves, cyclic = std::move(cyclic)](int64_t index) { + return curves.evaluated_length_total_for_curve(index, cyclic[index]); + }); + + if (domain == ATTR_DOMAIN_CURVE) { + return lengths; + } + + if (domain == ATTR_DOMAIN_POINT) { + return component.attribute_try_adapt_domain<float>( + std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); + } + + return {}; +} + +CurveLengthFieldInput::CurveLengthFieldInput() + : GeometryFieldInput(CPPType::get<float>(), "Spline Length node") +{ + category_ = Category::Generated; +} + +GVArray CurveLengthFieldInput::get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const +{ + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + return construct_curve_length_gvarray(curve_component, domain); + } + return {}; +} + +uint64_t CurveLengthFieldInput::hash() const +{ + /* Some random constant hash. */ + return 3549623580; +} + +bool CurveLengthFieldInput::is_equal_to(const fn::FieldNode &other) const +{ + return dynamic_cast<const CurveLengthFieldInput *>(&other) != nullptr; +} + } // namespace blender::bke /** \} */ @@ -245,18 +308,18 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const Attri /** \name Attribute Access Helper Functions * \{ */ -int CurveComponent::attribute_domain_size(const AttributeDomain domain) const +int CurveComponent::attribute_domain_num(const AttributeDomain domain) const { if (curves_ == nullptr) { return 0; } - const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( + const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( curves_->geometry); if (domain == ATTR_DOMAIN_POINT) { - return geometry.points_num(); + return curves.points_num(); } if (domain == ATTR_DOMAIN_CURVE) { - return geometry.curves_num(); + return curves.curves_num(); } return 0; } diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 0dc6f486d28..e56a7ca4dd8 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -79,7 +79,7 @@ void InstancesComponent::add_instance(const int instance_handle, const float4x4 BLI_assert(instance_handle < references_.size()); instance_reference_handles_.append(instance_handle); instance_transforms_.append(transform); - attributes_.reallocate(this->instances_amount()); + attributes_.reallocate(this->instances_num()); } blender::Span<int> InstancesComponent::instance_reference_handles() const @@ -183,7 +183,7 @@ void InstancesComponent::remove_unused_references() using namespace blender; using namespace blender::bke; - const int tot_instances = this->instances_amount(); + const int tot_instances = this->instances_num(); const int tot_references_before = references_.size(); if (tot_instances == 0) { @@ -258,12 +258,12 @@ void InstancesComponent::remove_unused_references() }); } -int InstancesComponent::instances_amount() const +int InstancesComponent::instances_num() const { return instance_transforms_.size(); } -int InstancesComponent::references_amount() const +int InstancesComponent::references_num() const { return references_.size(); } @@ -358,7 +358,7 @@ blender::Span<int> InstancesComponent::almost_unique_ids() const } } else { - almost_unique_ids_.reinitialize(this->instances_amount()); + almost_unique_ids_.reinitialize(this->instances_num()); for (const int i : almost_unique_ids_.index_range()) { almost_unique_ids_[i] = i; } @@ -366,12 +366,12 @@ blender::Span<int> InstancesComponent::almost_unique_ids() const return almost_unique_ids_; } -int InstancesComponent::attribute_domain_size(const AttributeDomain domain) const +int InstancesComponent::attribute_domain_num(const AttributeDomain domain) const { if (domain != ATTR_DOMAIN_INSTANCE) { return 0; } - return this->instances_amount(); + return this->instances_num(); } blender::bke::CustomDataAttributes &InstancesComponent::attributes() diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index fb39861d3e7..5ac9a03f43c 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -169,7 +169,7 @@ VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component, /** \name Attribute Access * \{ */ -int MeshComponent::attribute_domain_size(const AttributeDomain domain) const +int MeshComponent::attribute_domain_num(const AttributeDomain domain) const { if (mesh_ == nullptr) { return 0; @@ -839,20 +839,20 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com namespace blender::bke { template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> -static GVArray make_derived_read_attribute(const void *data, const int domain_size) +static GVArray make_derived_read_attribute(const void *data, const int domain_num) { return VArray<ElemT>::template ForDerivedSpan<StructT, GetFunc>( - Span<StructT>((const StructT *)data, domain_size)); + Span<StructT>((const StructT *)data, domain_num)); } template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &), void (*SetFunc)(StructT &, ElemT)> -static GVMutableArray make_derived_write_attribute(void *data, const int domain_size) +static GVMutableArray make_derived_write_attribute(void *data, const int domain_num) { return VMutableArray<ElemT>::template ForDerivedSpan<StructT, GetFunc, SetFunc>( - MutableSpan<StructT>((StructT *)data, domain_size)); + MutableSpan<StructT>((StructT *)data, domain_num)); } static float3 get_vertex_position(const MVert &vert) @@ -1160,7 +1160,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { bool exists(const GeometryComponent &component) const final { - return component.attribute_domain_size(ATTR_DOMAIN_FACE) != 0; + return component.attribute_domain_num(ATTR_DOMAIN_FACE) != 0; } }; diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index 400e0ea5e15..6de123c7cb9 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -104,7 +104,7 @@ void PointCloudComponent::ensure_owns_direct_data() /** \name Attribute Access * \{ */ -int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) const +int PointCloudComponent::attribute_domain_num(const AttributeDomain domain) const { if (pointcloud_ == nullptr) { return 0; diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 2bd8b643899..40e36ced199 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -272,7 +272,7 @@ bool GeometrySet::has_pointcloud() const bool GeometrySet::has_instances() const { const InstancesComponent *component = this->get_component_for_read<InstancesComponent>(); - return component != nullptr && component->instances_amount() >= 1; + return component != nullptr && component->instances_num() >= 1; } bool GeometrySet::has_volume() const diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 84621be1960..35b514ac1a0 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -2803,7 +2803,7 @@ void BKE_gpencil_update_layer_transforms(const Depsgraph *depsgraph, Object *ob) /* Iterate over frame range. */ for (bGPDframe *gpf = gpf_start; gpf != NULL && gpf != gpf_end; gpf = gpf->next) { - /* Skip frames without a valid onion skinning id (note: active frame has one). */ + /* Skip frames without a valid onion skinning id (NOTE: active frame has one). */ if (gpf->runtime.onion_id == INT_MAX) { continue; } diff --git a/source/blender/blenkernel/intern/icons.cc b/source/blender/blenkernel/intern/icons.cc index 2ba6510ee71..f59f5352aad 100644 --- a/source/blender/blenkernel/intern/icons.cc +++ b/source/blender/blenkernel/intern/icons.cc @@ -114,32 +114,35 @@ static void icon_free(void *val) static void icon_free_data(int icon_id, Icon *icon) { - if (icon->obj_type == ICON_DATA_ID) { - ((ID *)(icon->obj))->icon_id = 0; - } - else if (icon->obj_type == ICON_DATA_IMBUF) { - ImBuf *imbuf = (ImBuf *)icon->obj; - if (imbuf) { - IMB_freeImBuf(imbuf); + switch (icon->obj_type) { + case ICON_DATA_ID: + ((ID *)(icon->obj))->icon_id = 0; + break; + case ICON_DATA_IMBUF: { + ImBuf *imbuf = (ImBuf *)icon->obj; + if (imbuf) { + IMB_freeImBuf(imbuf); + } + break; } - } - else if (icon->obj_type == ICON_DATA_PREVIEW) { - ((PreviewImage *)(icon->obj))->icon_id = 0; - } - else if (icon->obj_type == ICON_DATA_GPLAYER) { - ((bGPDlayer *)(icon->obj))->runtime.icon_id = 0; - } - else if (icon->obj_type == ICON_DATA_GEOM) { - ((struct Icon_Geom *)(icon->obj))->icon_id = 0; - } - else if (icon->obj_type == ICON_DATA_STUDIOLIGHT) { - StudioLight *sl = (StudioLight *)icon->obj; - if (sl != nullptr) { - BKE_studiolight_unset_icon_id(sl, icon_id); + case ICON_DATA_PREVIEW: + ((PreviewImage *)(icon->obj))->icon_id = 0; + break; + case ICON_DATA_GPLAYER: + ((bGPDlayer *)(icon->obj))->runtime.icon_id = 0; + break; + case ICON_DATA_GEOM: + ((struct Icon_Geom *)(icon->obj))->icon_id = 0; + break; + case ICON_DATA_STUDIOLIGHT: { + StudioLight *sl = (StudioLight *)icon->obj; + if (sl != nullptr) { + BKE_studiolight_unset_icon_id(sl, icon_id); + } + break; } - } - else { - BLI_assert(0); + default: + BLI_assert_unreachable(); } } @@ -241,16 +244,16 @@ static PreviewImage *previewimg_create_ex(size_t deferred_data_size) return prv_img; } -static PreviewImage *previewimg_deferred_create(const char *path, int source) +static PreviewImage *previewimg_deferred_create(const char *filepath, int source) { - /* We pack needed data for lazy loading (source type, in a single char, and path). */ - const size_t deferred_data_size = strlen(path) + 2; + /* We pack needed data for lazy loading (source type, in a single char, and filepath). */ + const size_t deferred_data_size = strlen(filepath) + 2; char *deferred_data; PreviewImage *prv = previewimg_create_ex(deferred_data_size); deferred_data = (char *)PRV_DEFERRED_DATA(prv); deferred_data[0] = source; - memcpy(&deferred_data[1], path, deferred_data_size - 1); + memcpy(&deferred_data[1], filepath, deferred_data_size - 1); return prv; } @@ -393,7 +396,7 @@ PreviewImage *BKE_previewimg_id_ensure(ID *id) return nullptr; } -void BKE_previewimg_id_custom_set(ID *id, const char *path) +void BKE_previewimg_id_custom_set(ID *id, const char *filepath) { PreviewImage **prv = BKE_previewimg_id_get_p(id); @@ -403,7 +406,7 @@ void BKE_previewimg_id_custom_set(ID *id, const char *path) if (*prv) { BKE_previewimg_deferred_release(*prv); } - *prv = previewimg_deferred_create(path, THB_SOURCE_IMAGE); + *prv = previewimg_deferred_create(filepath, THB_SOURCE_IMAGE); /* Can't lazy-render the preview on access. ID previews are saved to files and we want them to be * there in time. Not only if something happened to have accessed it meanwhile. */ @@ -458,7 +461,7 @@ PreviewImage *BKE_previewimg_cached_ensure(const char *name) } PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name, - const char *path, + const char *filepath, const int source, bool force_update) { @@ -476,8 +479,8 @@ PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name, if (prv && force_update) { const char *prv_deferred_data = (char *)PRV_DEFERRED_DATA(prv); - if (((int)prv_deferred_data[0] == source) && STREQ(&prv_deferred_data[1], path)) { - /* If same path, no need to re-allocate preview, just clear it up. */ + if (((int)prv_deferred_data[0] == source) && STREQ(&prv_deferred_data[1], filepath)) { + /* If same filepath, no need to re-allocate preview, just clear it up. */ BKE_previewimg_clear(prv); } else { @@ -486,7 +489,7 @@ PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name, } if (!prv) { - prv = previewimg_deferred_create(path, source); + prv = previewimg_deferred_create(filepath, source); force_update = true; } @@ -521,10 +524,10 @@ void BKE_previewimg_ensure(PreviewImage *prv, const int size) ImBuf *thumb; char *prv_deferred_data = (char *)PRV_DEFERRED_DATA(prv); int source = prv_deferred_data[0]; - char *path = &prv_deferred_data[1]; + char *filepath = &prv_deferred_data[1]; int icon_w, icon_h; - thumb = IMB_thumb_manage(path, THB_LARGE, (ThumbSource)source); + thumb = IMB_thumb_manage(filepath, THB_LARGE, (ThumbSource)source); if (thumb) { /* PreviewImage assumes premultiplied alhpa... */ diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 7b8dfdc690c..e99230ef523 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -872,7 +872,7 @@ bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is case IDP_ID: return (IDP_Id(prop1) == IDP_Id(prop2)); default: - BLI_assert(0); + BLI_assert_unreachable(); break; } diff --git a/source/blender/blenkernel/intern/idprop_utils.c b/source/blender/blenkernel/intern/idprop_utils.c index 2ffa5125b1d..5b7484ab422 100644 --- a/source/blender/blenkernel/intern/idprop_utils.c +++ b/source/blender/blenkernel/intern/idprop_utils.c @@ -181,7 +181,7 @@ static void idp_repr_fn_recursive(struct ReprState *state, const IDProperty *pro break; } default: { - BLI_assert(0); + BLI_assert_unreachable(); break; } } diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index dfa820519a5..3d894f47ae0 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -721,6 +721,9 @@ static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src) imapf_src = imapf_src->next) { ImagePackedFile *imapf_dst = static_cast<ImagePackedFile *>( MEM_mallocN(sizeof(ImagePackedFile), "Image Packed Files (copy)")); + + imapf_dst->view = imapf_src->view; + imapf_dst->tile_number = imapf_src->tile_number; STRNCPY(imapf_dst->filepath, imapf_src->filepath); if (imapf_src->packedfile) { @@ -1197,7 +1200,8 @@ Image *BKE_image_add_from_imbuf(Main *bmain, ImBuf *ibuf, const char *name) } /** Pack image buffer to memory as PNG or EXR. */ -static bool image_memorypack_imbuf(Image *ima, ImBuf *ibuf, const char *filepath) +static bool image_memorypack_imbuf( + Image *ima, ImBuf *ibuf, int view, int tile_number, const char *filepath) { ibuf->ftype = (ibuf->rect_float) ? IMB_FTYPE_OPENEXR : IMB_FTYPE_PNG; @@ -1219,6 +1223,8 @@ static bool image_memorypack_imbuf(Image *ima, ImBuf *ibuf, const char *filepath imapf = static_cast<ImagePackedFile *>(MEM_mallocN(sizeof(ImagePackedFile), "Image PackedFile")); STRNCPY(imapf->filepath, filepath); imapf->packedfile = pf; + imapf->view = view; + imapf->tile_number = tile_number; BLI_addtail(&ima->packedfiles, imapf); ibuf->encodedbuffer = nullptr; @@ -1234,42 +1240,47 @@ bool BKE_image_memorypack(Image *ima) image_free_packedfiles(ima); - if (BKE_image_is_multiview(ima)) { - /* Store each view as a separate packed files with R_IMF_VIEWS_INDIVIDUAL. */ - ImageView *iv; - int i; + const int tot_viewfiles = image_num_viewfiles(ima); + const bool is_tiled = (ima->source == IMA_SRC_TILED); + const bool is_multiview = BKE_image_is_multiview(ima); - for (i = 0, iv = static_cast<ImageView *>(ima->views.first); iv; - iv = static_cast<ImageView *>(iv->next), i++) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, 0, nullptr); + ImageUser iuser{}; + BKE_imageuser_default(&iuser); + char tiled_filepath[FILE_MAX]; + for (int view = 0; view < tot_viewfiles; view++) { + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + int index = (is_multiview || is_tiled) ? view : IMA_NO_INDEX; + int entry = is_tiled ? tile->tile_number : 0; + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, nullptr); if (!ibuf) { ok = false; break; } - /* if the image was a R_IMF_VIEWS_STEREO_3D we force _L, _R suffices */ - if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { - const char *suffix[2] = {STEREO_LEFT_SUFFIX, STEREO_RIGHT_SUFFIX}; - BLI_path_suffix(iv->filepath, FILE_MAX, suffix[i], ""); + const char *filepath = ibuf->name; + if (is_tiled) { + iuser.tile = tile->tile_number; + BKE_image_user_file_path(&iuser, ima, tiled_filepath); + filepath = tiled_filepath; + } + else if (is_multiview) { + ImageView *iv = static_cast<ImageView *>(BLI_findlink(&ima->views, view)); + /* if the image was a R_IMF_VIEWS_STEREO_3D we force _L, _R suffices */ + if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { + const char *suffix[2] = {STEREO_LEFT_SUFFIX, STEREO_RIGHT_SUFFIX}; + BLI_path_suffix(iv->filepath, FILE_MAX, suffix[view], ""); + } + filepath = iv->filepath; } - ok = ok && image_memorypack_imbuf(ima, ibuf, iv->filepath); + ok = ok && image_memorypack_imbuf(ima, ibuf, view, tile->tile_number, filepath); IMB_freeImBuf(ibuf); } - - ima->views_format = R_IMF_VIEWS_INDIVIDUAL; } - else { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, nullptr); - if (ibuf) { - ok = ok && image_memorypack_imbuf(ima, ibuf, ibuf->name); - IMB_freeImBuf(ibuf); - } - else { - ok = false; - } + if (is_multiview) { + ima->views_format = R_IMF_VIEWS_INDIVIDUAL; } if (ok && ima->source == IMA_SRC_GENERATED) { @@ -1284,27 +1295,24 @@ void BKE_image_packfiles(ReportList *reports, Image *ima, const char *basepath) { const int tot_viewfiles = image_num_viewfiles(ima); - if (tot_viewfiles == 1) { - ImagePackedFile *imapf = static_cast<ImagePackedFile *>( - MEM_mallocN(sizeof(ImagePackedFile), "Image packed file")); - BLI_addtail(&ima->packedfiles, imapf); - imapf->packedfile = BKE_packedfile_new(reports, ima->filepath, basepath); - if (imapf->packedfile) { - STRNCPY(imapf->filepath, ima->filepath); - } - else { - BLI_freelinkN(&ima->packedfiles, imapf); - } - } - else { - for (ImageView *iv = static_cast<ImageView *>(ima->views.first); iv; iv = iv->next) { + ImageUser iuser{}; + BKE_imageuser_default(&iuser); + for (int view = 0; view < tot_viewfiles; view++) { + iuser.view = view; + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + iuser.tile = tile->tile_number; + char filepath[FILE_MAX]; + BKE_image_user_file_path(&iuser, ima, filepath); + ImagePackedFile *imapf = static_cast<ImagePackedFile *>( MEM_mallocN(sizeof(ImagePackedFile), "Image packed file")); BLI_addtail(&ima->packedfiles, imapf); - imapf->packedfile = BKE_packedfile_new(reports, iv->filepath, basepath); + imapf->packedfile = BKE_packedfile_new(reports, filepath, basepath); + imapf->view = view; + imapf->tile_number = tile->tile_number; if (imapf->packedfile) { - STRNCPY(imapf->filepath, iv->filepath); + STRNCPY(imapf->filepath, filepath); } else { BLI_freelinkN(&ima->packedfiles, imapf); @@ -1323,11 +1331,16 @@ void BKE_image_packfiles_from_mem(ReportList *reports, if (tot_viewfiles != 1) { BKE_report(reports, RPT_ERROR, "Cannot pack multiview images from raw data currently..."); } + else if (ima->source == IMA_SRC_TILED) { + BKE_report(reports, RPT_ERROR, "Cannot pack tiled images from raw data currently..."); + } else { ImagePackedFile *imapf = static_cast<ImagePackedFile *>( MEM_mallocN(sizeof(ImagePackedFile), __func__)); BLI_addtail(&ima->packedfiles, imapf); imapf->packedfile = BKE_packedfile_new_from_memory(data, data_len); + imapf->view = 0; + imapf->tile_number = 1001; STRNCPY(imapf->filepath, ima->filepath); } } @@ -2950,8 +2963,9 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) /* try to repack file */ if (BKE_image_has_packedfile(ima)) { const int tot_viewfiles = image_num_viewfiles(ima); + const int tot_files = tot_viewfiles * BLI_listbase_count(&ima->tiles); - if (tot_viewfiles != BLI_listbase_count_at_most(&ima->packedfiles, tot_viewfiles + 1)) { + if (tot_files != BLI_listbase_count_at_most(&ima->packedfiles, tot_files + 1)) { /* in case there are new available files to be loaded */ image_free_packedfiles(ima); BKE_image_packfiles(nullptr, ima, ID_BLEND_PATH(bmain, &ima->id)); @@ -3926,18 +3940,23 @@ static ImBuf *load_image_single(Image *ima, int flag = IB_rect | IB_multilayer; *r_cache_ibuf = true; + const int tile_number = image_get_tile_number_from_iuser(ima, iuser); /* is there a PackedFile with this image ? */ if (has_packed && !is_sequence) { - ImagePackedFile *imapf = static_cast<ImagePackedFile *>( - BLI_findlink(&ima->packedfiles, view_id)); - if (imapf->packedfile) { - flag |= imbuf_alpha_flags_for_image(ima); - ibuf = IMB_ibImageFromMemory((unsigned char *)imapf->packedfile->data, - imapf->packedfile->size, - flag, - ima->colorspace_settings.name, - "<packed data>"); + flag |= imbuf_alpha_flags_for_image(ima); + + LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) { + if (imapf->view == view_id && imapf->tile_number == tile_number) { + if (imapf->packedfile) { + ibuf = IMB_ibImageFromMemory((unsigned char *)imapf->packedfile->data, + imapf->packedfile->size, + flag, + ima->colorspace_settings.name, + "<packed data>"); + } + break; + } } } else { @@ -3996,6 +4015,8 @@ static ImBuf *load_image_single(Image *ima, BLI_addtail(&ima->packedfiles, imapf); STRNCPY(imapf->filepath, filepath); + imapf->view = view_id; + imapf->tile_number = tile_number; imapf->packedfile = BKE_packedfile_new( nullptr, filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); } @@ -5103,7 +5124,7 @@ bool BKE_image_has_packedfile(const Image *ima) return (BLI_listbase_is_empty(&ima->packedfiles) == false); } -bool BKE_image_has_filepath(Image *ima) +bool BKE_image_has_filepath(const Image *ima) { /* This could be improved to detect cases like //../../, currently path * remapping empty file paths empty. */ diff --git a/source/blender/blenkernel/intern/image_format.cc b/source/blender/blenkernel/intern/image_format.cc index 30be1fdaba7..57763e1670f 100644 --- a/source/blender/blenkernel/intern/image_format.cc +++ b/source/blender/blenkernel/intern/image_format.cc @@ -911,6 +911,11 @@ void BKE_image_format_from_imbuf(ImageFormatData *im_format, const ImBuf *imbuf) im_format->planes = imbuf->planes; } +bool BKE_image_format_is_byte(const ImageFormatData *imf) +{ + return (imf->depth == R_IMF_CHAN_DEPTH_8) && (BKE_imtype_valid_depths(imf->imtype) & imf->depth); +} + /* Color Management */ void BKE_image_format_color_management_copy(ImageFormatData *imf, const ImageFormatData *imf_src) diff --git a/source/blender/blenkernel/intern/image_save.cc b/source/blender/blenkernel/intern/image_save.cc index 5361f234a63..0230cf89d06 100644 --- a/source/blender/blenkernel/intern/image_save.cc +++ b/source/blender/blenkernel/intern/image_save.cc @@ -23,6 +23,7 @@ #include "IMB_openexr.h" #include "BKE_colortools.h" +#include "BKE_global.h" #include "BKE_image.h" #include "BKE_image_format.h" #include "BKE_image_save.h" @@ -34,14 +35,216 @@ using blender::Vector; -void BKE_image_save_options_init(ImageSaveOptions *opts, Main *bmain, Scene *scene) +static char imtype_best_depth(ImBuf *ibuf, const char imtype) { + const char depth_ok = BKE_imtype_valid_depths(imtype); + + if (ibuf->rect_float) { + if (depth_ok & R_IMF_CHAN_DEPTH_32) { + return R_IMF_CHAN_DEPTH_32; + } + if (depth_ok & R_IMF_CHAN_DEPTH_24) { + return R_IMF_CHAN_DEPTH_24; + } + if (depth_ok & R_IMF_CHAN_DEPTH_16) { + return R_IMF_CHAN_DEPTH_16; + } + if (depth_ok & R_IMF_CHAN_DEPTH_12) { + return R_IMF_CHAN_DEPTH_12; + } + return R_IMF_CHAN_DEPTH_8; + } + + if (depth_ok & R_IMF_CHAN_DEPTH_8) { + return R_IMF_CHAN_DEPTH_8; + } + if (depth_ok & R_IMF_CHAN_DEPTH_12) { + return R_IMF_CHAN_DEPTH_12; + } + if (depth_ok & R_IMF_CHAN_DEPTH_16) { + return R_IMF_CHAN_DEPTH_16; + } + if (depth_ok & R_IMF_CHAN_DEPTH_24) { + return R_IMF_CHAN_DEPTH_24; + } + if (depth_ok & R_IMF_CHAN_DEPTH_32) { + return R_IMF_CHAN_DEPTH_32; + } + return R_IMF_CHAN_DEPTH_8; /* fallback, should not get here */ +} + +bool BKE_image_save_options_init(ImageSaveOptions *opts, + Main *bmain, + Scene *scene, + Image *ima, + ImageUser *iuser, + const bool guess_path) +{ + /* For saving a tiled image we need an iuser, so use a local one if there isn't already one. */ + ImageUser save_iuser; + if (iuser == nullptr) { + BKE_imageuser_default(&save_iuser); + iuser = &save_iuser; + iuser->scene = scene; + } + memset(opts, 0, sizeof(*opts)); opts->bmain = bmain; opts->scene = scene; + opts->save_as_render = ima->source == IMA_SRC_VIEWER; BKE_image_format_init(&opts->im_format, false); + + void *lock; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock); + + if (ibuf) { + Scene *scene = opts->scene; + bool is_depth_set = false; + const char *ima_colorspace = ima->colorspace_settings.name; + + if (ELEM(ima->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { + /* imtype */ + BKE_image_format_init_for_write(&opts->im_format, scene, NULL); + is_depth_set = true; + if (!BKE_image_is_multiview(ima)) { + /* In case multiview is disabled, + * render settings would be invalid for render result in this area. */ + opts->im_format.stereo3d_format = *ima->stereo3d_format; + opts->im_format.views_format = ima->views_format; + } + } + else { + if (ima->source == IMA_SRC_GENERATED) { + opts->im_format.imtype = R_IMF_IMTYPE_PNG; + opts->im_format.compress = ibuf->foptions.quality; + opts->im_format.planes = ibuf->planes; + if (!IMB_colormanagement_space_name_is_data(ima_colorspace)) { + ima_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE); + } + } + else { + BKE_image_format_from_imbuf(&opts->im_format, ibuf); + } + + /* use the multiview image settings as the default */ + opts->im_format.stereo3d_format = *ima->stereo3d_format; + opts->im_format.views_format = ima->views_format; + + /* Render output: colorspace from render settings. */ + BKE_image_format_color_management_copy_from_scene(&opts->im_format, scene); + } + + /* Default to saving in the same colorspace as the image setting. */ + if (!opts->save_as_render) { + STRNCPY(opts->im_format.linear_colorspace_settings.name, ima_colorspace); + } + + opts->im_format.color_management = R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE; + + if (ima->source == IMA_SRC_TILED) { + BLI_strncpy(opts->filepath, ima->filepath, sizeof(opts->filepath)); + BLI_path_abs(opts->filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); + } + else { + BLI_strncpy(opts->filepath, ibuf->name, sizeof(opts->filepath)); + } + + /* sanitize all settings */ + + /* unlikely but just in case */ + if (ELEM(opts->im_format.planes, R_IMF_PLANES_BW, R_IMF_PLANES_RGB, R_IMF_PLANES_RGBA) == 0) { + opts->im_format.planes = R_IMF_PLANES_RGBA; + } + + /* depth, account for float buffer and format support */ + if (is_depth_set == false) { + opts->im_format.depth = imtype_best_depth(ibuf, opts->im_format.imtype); + } + + /* some formats don't use quality so fallback to scenes quality */ + if (opts->im_format.quality == 0) { + opts->im_format.quality = scene->r.im_format.quality; + } + + /* check for empty path */ + if (guess_path && opts->filepath[0] == 0) { + const bool is_prev_save = !STREQ(G.ima, "//"); + if (opts->save_as_render) { + if (is_prev_save) { + BLI_strncpy(opts->filepath, G.ima, sizeof(opts->filepath)); + } + else { + BLI_strncpy(opts->filepath, "//untitled", sizeof(opts->filepath)); + BLI_path_abs(opts->filepath, BKE_main_blendfile_path(bmain)); + } + } + else { + BLI_snprintf(opts->filepath, sizeof(opts->filepath), "//%s", ima->id.name + 2); + BLI_path_make_safe(opts->filepath); + BLI_path_abs(opts->filepath, is_prev_save ? G.ima : BKE_main_blendfile_path(bmain)); + } + + /* append UDIM marker if not present */ + if (ima->source == IMA_SRC_TILED && strstr(opts->filepath, "<UDIM>") == NULL) { + int len = strlen(opts->filepath); + STR_CONCAT(opts->filepath, len, ".<UDIM>"); + } + } + } + + /* Copy for detecting UI changes. */ + opts->prev_save_as_render = opts->save_as_render; + opts->prev_imtype = opts->im_format.imtype; + + BKE_image_release_ibuf(ima, ibuf, lock); + + return (ibuf != NULL); +} + +void BKE_image_save_options_update(ImageSaveOptions *opts, Image *image) +{ + /* Auto update color space when changing save as render and file type. */ + if (opts->save_as_render) { + if (!opts->prev_save_as_render) { + if (ELEM(image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { + BKE_image_format_init_for_write(&opts->im_format, opts->scene, NULL); + } + else { + BKE_image_format_color_management_copy_from_scene(&opts->im_format, opts->scene); + } + } + } + else { + if (opts->prev_save_as_render) { + /* Copy colorspace from image settings. */ + BKE_color_managed_colorspace_settings_copy(&opts->im_format.linear_colorspace_settings, + &image->colorspace_settings); + } + else if (opts->im_format.imtype != opts->prev_imtype && + !IMB_colormanagement_space_name_is_data( + opts->im_format.linear_colorspace_settings.name)) { + const bool linear_float_output = BKE_imtype_requires_linear_float(opts->im_format.imtype); + + /* TODO: detect if the colorspace is linear, not just equal to scene linear. */ + const bool is_linear = IMB_colormanagement_space_name_is_scene_linear( + opts->im_format.linear_colorspace_settings.name); + + /* If changing to a linear float or byte format, ensure we have a compatible color space. */ + if (linear_float_output && !is_linear) { + STRNCPY(opts->im_format.linear_colorspace_settings.name, + IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_FLOAT)); + } + else if (!linear_float_output && is_linear) { + STRNCPY(opts->im_format.linear_colorspace_settings.name, + IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE)); + } + } + } + + opts->prev_save_as_render = opts->save_as_render; + opts->prev_imtype = opts->im_format.imtype; } void BKE_image_save_options_free(ImageSaveOptions *opts) @@ -105,12 +308,17 @@ static void image_save_post(ReportList *reports, BLI_path_rel(ima->filepath, relbase); /* only after saving */ } - ColorManagedColorspaceSettings old_colorspace_settings; - BKE_color_managed_colorspace_settings_copy(&old_colorspace_settings, &ima->colorspace_settings); - IMB_colormanagement_colorspace_from_ibuf_ftype(&ima->colorspace_settings, ibuf); - if (!BKE_color_managed_colorspace_settings_equals(&old_colorspace_settings, - &ima->colorspace_settings)) { - *r_colorspace_changed = true; + /* Update image file color space when saving to another color space. */ + const bool linear_float_output = BKE_imtype_requires_linear_float(opts->im_format.imtype); + + if (!opts->save_as_render || linear_float_output) { + if (opts->im_format.linear_colorspace_settings.name[0] && + !BKE_color_managed_colorspace_settings_equals( + &ima->colorspace_settings, &opts->im_format.linear_colorspace_settings)) { + BKE_color_managed_colorspace_settings_copy(&ima->colorspace_settings, + &opts->im_format.linear_colorspace_settings); + *r_colorspace_changed = true; + } } } @@ -176,12 +384,12 @@ static bool image_save_single(ReportList *reports, /* we need renderresult for exr and rendered multiview */ rr = BKE_image_acquire_renderresult(opts->scene, ima); - bool is_mono = rr ? BLI_listbase_count_at_most(&rr->views, 2) < 2 : - BLI_listbase_count_at_most(&ima->views, 2) < 2; - bool is_exr_rr = rr && ELEM(imf->imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) && - RE_HasFloatPixels(rr); - bool is_multilayer = is_exr_rr && (imf->imtype == R_IMF_IMTYPE_MULTILAYER); - int layer = (iuser && !is_multilayer) ? iuser->layer : -1; + const bool is_mono = rr ? BLI_listbase_count_at_most(&rr->views, 2) < 2 : + BLI_listbase_count_at_most(&ima->views, 2) < 2; + const bool is_exr_rr = rr && ELEM(imf->imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) && + RE_HasFloatPixels(rr); + const bool is_multilayer = is_exr_rr && (imf->imtype == R_IMF_IMTYPE_MULTILAYER); + const int layer = (iuser && !is_multilayer) ? iuser->layer : -1; /* error handling */ if (rr == nullptr) { @@ -366,7 +574,6 @@ static bool image_save_single(ReportList *reports, colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, imf); BKE_image_format_to_imbuf(colormanaged_ibuf, imf); - IMB_prepare_write_ImBuf(IMB_isfloat(colormanaged_ibuf), colormanaged_ibuf); /* duplicate buffer to prevent locker issue when using render result */ ibuf_stereo[i] = IMB_dupImBuf(colormanaged_ibuf); @@ -401,8 +608,13 @@ static bool image_save_single(ReportList *reports, bool BKE_image_save( ReportList *reports, Main *bmain, Image *ima, ImageUser *iuser, ImageSaveOptions *opts) { + /* For saving a tiled image we need an iuser, so use a local one if there isn't already one. */ ImageUser save_iuser; - BKE_imageuser_default(&save_iuser); + if (iuser == nullptr) { + BKE_imageuser_default(&save_iuser); + iuser = &save_iuser; + iuser->scene = opts->scene; + } bool colorspace_changed = false; @@ -419,12 +631,6 @@ bool BKE_image_save( opts->filepath); return false; } - - /* For saving a tiled image we need an iuser, so use a local one if there isn't already one. - */ - if (iuser == nullptr) { - iuser = &save_iuser; - } } /* Save images */ @@ -787,7 +993,6 @@ bool BKE_image_render_write(ReportList *reports, int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); ibuf_arr[i] = RE_render_result_rect_to_ibuf(rr, &image_format, dither, view_id); IMB_colormanagement_imbuf_for_write(ibuf_arr[i], true, false, &image_format); - IMB_prepare_write_ImBuf(IMB_isfloat(ibuf_arr[i]), ibuf_arr[i]); } ibuf_arr[2] = IMB_stereo3d_ImBuf(&image_format, ibuf_arr[0], ibuf_arr[1]); diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 27427b1fb44..2f8b3e00df9 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -263,7 +263,6 @@ void id_us_ensure_real(ID *id) "ID user count error: %s (from '%s')", id->name, id->lib ? id->lib->filepath_abs : "[Main]"); - BLI_assert(0); } id->us = limit + 1; id->tag |= LIB_TAG_EXTRAUSER_SET; @@ -321,7 +320,6 @@ void id_us_min(ID *id) id->lib ? id->lib->filepath_abs : "[Main]", id->us, limit); - BLI_assert(0); } id->us = limit; } diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index b83d30a27bf..dc112af41db 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1219,12 +1219,11 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int "Levels of dependency relationships between library overrides IDs is way too high, " "skipping further processing loops (involves at least '%s')", id->name); - BLI_assert(0); return NULL; } if (!ID_IS_OVERRIDE_LIBRARY(id)) { - BLI_assert(0); + BLI_assert_unreachable(); return NULL; } @@ -2088,9 +2087,7 @@ static bool lib_override_resync_tagging_finalize_recurse( CLOG_INFO(&LOG, 4, "Found root ID '%s' for resync root ID '%s'", id_root->name, id->name); - if (id_root->override_library == NULL) { - BLI_assert(0); - } + BLI_assert(id_root->override_library != NULL); LinkNodePair **id_resync_roots_p; if (!BLI_ghash_ensure_p(id_roots, id_root, (void ***)&id_resync_roots_p)) { @@ -2342,7 +2339,6 @@ static int lib_override_sort_libraries_func(LibraryIDLinkCallbackData *cb_data) "loops (Involves at least '%s' and '%s')", id_owner->lib->filepath, id->lib->filepath); - BLI_assert(0); return IDWALK_RET_NOP; } diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 971db852463..2600a40153c 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -75,6 +75,7 @@ static void foreach_libblock_remap_callback_skip(const ID *UNUSED(id_owner), { ID *id = *id_ptr; BLI_assert(id != NULL); + if (is_indirect) { id->runtime.remap.skipped_indirect++; } @@ -82,8 +83,9 @@ static void foreach_libblock_remap_callback_skip(const ID *UNUSED(id_owner), id->runtime.remap.skipped_direct++; } else { - BLI_assert(0); + BLI_assert_unreachable(); } + if (cb_flag & IDWALK_CB_USER) { id->runtime.remap.skipped_refcounted++; } @@ -553,7 +555,6 @@ static void libblock_remap_foreach_idpair_cb(ID *old_id, ID *new_id, void *user_ new_id ? new_id->name : "<NULL>", new_id, old_id->us - skipped_refcounted); - BLI_assert(0); } const int skipped_direct = old_id->runtime.remap.skipped_direct; diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index 03e03dacfbc..b9ed783fa8c 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -502,13 +502,13 @@ BlendThumbnail *BKE_main_thumbnail_from_imbuf(Main *bmain, ImBuf *img) } if (img) { - const size_t sz = BLEN_THUMB_MEMSIZE(img->x, img->y); - data = MEM_mallocN(sz, __func__); + const size_t data_size = BLEN_THUMB_MEMSIZE(img->x, img->y); + data = MEM_mallocN(data_size, __func__); IMB_rect_from_float(img); /* Just in case... */ data->width = img->x; data->height = img->y; - memcpy(data->rect, img->rect, sz - sizeof(*data)); + memcpy(data->rect, img->rect, data_size - sizeof(*data)); } if (bmain) { diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 002b496393f..e5b875cadf9 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -1123,15 +1123,16 @@ void BKE_object_material_remap_calc(Object *ob_dst, Object *ob_src, short *remap BLI_ghash_free(gh_mat_map, NULL, NULL); } -void BKE_object_material_from_eval_data(Main *bmain, Object *ob_orig, ID *data_eval) +void BKE_object_material_from_eval_data(Main *bmain, Object *ob_orig, const ID *data_eval) { ID *data_orig = ob_orig->data; short *orig_totcol = BKE_id_material_len_p(data_orig); Material ***orig_mat = BKE_id_material_array_p(data_orig); - short *eval_totcol = BKE_id_material_len_p(data_eval); - Material ***eval_mat = BKE_id_material_array_p(data_eval); + /* Can cast away const, because the data is not changed. */ + const short *eval_totcol = BKE_id_material_len_p((ID *)data_eval); + Material ***eval_mat = BKE_id_material_array_p((ID *)data_eval); if (ELEM(NULL, orig_totcol, orig_mat, eval_totcol, eval_mat)) { return; @@ -1244,7 +1245,6 @@ bool BKE_object_material_slot_remove(Main *bmain, Object *ob) /* this should never happen and used to crash */ if (ob->actcol <= 0) { CLOG_ERROR(&LOG, "invalid material index %d, report a bug!", ob->actcol); - BLI_assert(0); return false; } diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 628f59ae449..05baf156099 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -1577,6 +1577,19 @@ void BKE_mesh_smooth_flag_set(Mesh *me, const bool use_smooth) } } +void BKE_mesh_auto_smooth_flag_set(Mesh *me, + const bool use_auto_smooth, + const float auto_smooth_angle) +{ + if (use_auto_smooth) { + me->flag |= ME_AUTOSMOOTH; + me->smoothresh = auto_smooth_angle; + } + else { + me->flag &= ~ME_AUTOSMOOTH; + } +} + int poly_find_loop_from_vert(const MPoly *poly, const MLoop *loopstart, uint vert) { for (int j = 0; j < poly->totloop; j++, loopstart++) { @@ -1809,7 +1822,7 @@ void BKE_mesh_mselect_validate(Mesh *me) break; } default: { - BLI_assert(0); + BLI_assert_unreachable(); break; } } diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 0038a2aa806..07c9bccacc3 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -12,6 +12,7 @@ #include "DNA_key_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_meta_types.h" #include "DNA_object_types.h" #include "DNA_pointcloud_types.h" @@ -1385,7 +1386,7 @@ static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int act for (i = 0; i < tot; i++) { CustomDataLayer *layer = &mesh_src->vdata.layers[CustomData_get_layer_index_n(&mesh_src->vdata, CD_SHAPEKEY, i)]; - float(*cos)[3], (*kbcos)[3]; + float(*kbcos)[3]; for (kb = (KeyBlock *)mesh_dst->key->block.first; kb; kb = kb->next) { if (kb->uid == layer->uid) { @@ -1402,7 +1403,8 @@ static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int act MEM_freeN(kb->data); } - cos = (float(*)[3])CustomData_get_layer_n(&mesh_src->vdata, CD_SHAPEKEY, i); + const float(*cos)[3] = (const float(*)[3])CustomData_get_layer_n( + &mesh_src->vdata, CD_SHAPEKEY, i); kb->totelem = mesh_src->totvert; kb->data = kbcos = (float(*)[3])MEM_malloc_arrayN(kb->totelem, sizeof(float[3]), __func__); diff --git a/source/blender/blenkernel/intern/mesh_evaluate.cc b/source/blender/blenkernel/intern/mesh_evaluate.cc index ec2660a0145..de0489d668f 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.cc +++ b/source/blender/blenkernel/intern/mesh_evaluate.cc @@ -18,8 +18,9 @@ #include "BLI_alloca.h" #include "BLI_bitmap.h" #include "BLI_edgehash.h" - +#include "BLI_index_range.hh" #include "BLI_math.h" +#include "BLI_span.hh" #include "BLI_utildefines.h" #include "BKE_customdata.h" @@ -27,6 +28,10 @@ #include "BKE_mesh.h" #include "BKE_multires.h" +using blender::IndexRange; +using blender::MutableSpan; +using blender::Span; + /* -------------------------------------------------------------------- */ /** \name Polygon Calculations * \{ */ @@ -644,7 +649,7 @@ static void bm_corners_to_loops_ex(ID *id, MFace *mf = mface + findex; for (int i = 0; i < numTex; i++) { - MTFace *texface = (MTFace *)CustomData_get_n(fdata, CD_MTFACE, findex, i); + const MTFace *texface = (const MTFace *)CustomData_get_n(fdata, CD_MTFACE, findex, i); MLoopUV *mloopuv = (MLoopUV *)CustomData_get_n(ldata, CD_MLOOPUV, loopstart, i); copy_v2_v2(mloopuv->uv, texface->uv[0]); @@ -662,7 +667,7 @@ static void bm_corners_to_loops_ex(ID *id, for (int i = 0; i < numCol; i++) { MLoopCol *mloopcol = (MLoopCol *)CustomData_get_n(ldata, CD_PROP_BYTE_COLOR, loopstart, i); - MCol *mcol = (MCol *)CustomData_get_n(fdata, CD_MCOL, findex, i); + const MCol *mcol = (const MCol *)CustomData_get_n(fdata, CD_MCOL, findex, i); MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[0]); mloopcol++; @@ -678,7 +683,7 @@ static void bm_corners_to_loops_ex(ID *id, if (CustomData_has_layer(fdata, CD_TESSLOOPNORMAL)) { float(*lnors)[3] = (float(*)[3])CustomData_get(ldata, loopstart, CD_NORMAL); - short(*tlnors)[3] = (short(*)[3])CustomData_get(fdata, findex, CD_TESSLOOPNORMAL); + const short(*tlnors)[3] = (short(*)[3])CustomData_get(fdata, findex, CD_TESSLOOPNORMAL); const int max = mf->v4 ? 4 : 3; for (int i = 0; i < max; i++, lnors++, tlnors++) { @@ -688,8 +693,8 @@ static void bm_corners_to_loops_ex(ID *id, if (CustomData_has_layer(fdata, CD_MDISPS)) { MDisps *ld = (MDisps *)CustomData_get(ldata, loopstart, CD_MDISPS); - MDisps *fd = (MDisps *)CustomData_get(fdata, findex, CD_MDISPS); - float(*disps)[3] = fd->disps; + const MDisps *fd = (const MDisps *)CustomData_get(fdata, findex, CD_MDISPS); + const float(*disps)[3] = fd->disps; int tot = mf->v4 ? 4 : 3; int corners; @@ -1113,58 +1118,49 @@ void BKE_mesh_flush_select_from_polys(Mesh *me) me->mvert, me->totvert, me->mloop, me->medge, me->totedge, me->mpoly, me->totpoly); } -void BKE_mesh_flush_select_from_verts_ex(const MVert *mvert, - const int UNUSED(totvert), - const MLoop *mloop, - MEdge *medge, - const int totedge, - MPoly *mpoly, - const int totpoly) +static void mesh_flush_select_from_verts(const Span<MVert> verts, + const Span<MLoop> loops, + MutableSpan<MEdge> edges, + MutableSpan<MPoly> polys) { - MEdge *med; - MPoly *mp; - - /* edges */ - int i = totedge; - for (med = medge; i--; med++) { - if ((med->flag & ME_HIDE) == 0) { - if ((mvert[med->v1].flag & SELECT) && (mvert[med->v2].flag & SELECT)) { - med->flag |= SELECT; + for (const int i : edges.index_range()) { + if ((edges[i].flag & ME_HIDE) == 0) { + MEdge &edge = edges[i]; + if ((verts[edge.v1].flag & SELECT) && (verts[edge.v2].flag & SELECT)) { + edge.flag |= SELECT; } else { - med->flag &= ~SELECT; + edge.flag &= ~SELECT; } } } - /* polys */ - i = totpoly; - for (mp = mpoly; i--; mp++) { - if ((mp->flag & ME_HIDE) == 0) { - bool ok = true; - const MLoop *ml; - int j; - j = mp->totloop; - for (ml = &mloop[mp->loopstart]; j--; ml++) { - if ((mvert[ml->v].flag & SELECT) == 0) { - ok = false; - break; - } - } - - if (ok) { - mp->flag |= ME_FACE_SEL; - } - else { - mp->flag &= (char)~ME_FACE_SEL; + for (const int i : polys.index_range()) { + if (polys[i].flag & ME_HIDE) { + continue; + } + MPoly &poly = polys[i]; + bool all_verts_selected = true; + for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { + if (!(verts[loop.v].flag & SELECT)) { + all_verts_selected = false; } } + if (all_verts_selected) { + poly.flag |= ME_FACE_SEL; + } + else { + poly.flag &= (char)~ME_FACE_SEL; + } } } + void BKE_mesh_flush_select_from_verts(Mesh *me) { - BKE_mesh_flush_select_from_verts_ex( - me->mvert, me->totvert, me->mloop, me->medge, me->totedge, me->mpoly, me->totpoly); + mesh_flush_select_from_verts({me->mvert, me->totvert}, + {me->mloop, me->totloop}, + {me->medge, me->totedge}, + {me->mpoly, me->totpoly}); } /** \} */ diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index 02c9f61957d..c960a7f35f1 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -290,7 +290,7 @@ void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, Mesh *source) &target->vdata, CD_PAINT_MASK, CD_CALLOC, nullptr, target->totvert); } - float *source_mask; + const float *source_mask; if (CustomData_has_layer(&source->vdata, CD_PAINT_MASK)) { source_mask = (float *)CustomData_get_layer(&source->vdata, CD_PAINT_MASK); } diff --git a/source/blender/blenkernel/intern/mesh_tangent.c b/source/blender/blenkernel/intern/mesh_tangent.c index f2e46c3bd92..c0b2b33c47c 100644 --- a/source/blender/blenkernel/intern/mesh_tangent.c +++ b/source/blender/blenkernel/intern/mesh_tangent.c @@ -37,13 +37,13 @@ /* User data. */ typedef struct { - const MPoly *mpolys; /* faces */ - const MLoop *mloops; /* faces's vertices */ - const MVert *mverts; /* vertices */ - const MLoopUV *luvs; /* texture coordinates */ - float (*lnors)[3]; /* loops' normals */ - float (*tangents)[4]; /* output tangents */ - int num_polys; /* number of polygons */ + const MPoly *mpolys; /* faces */ + const MLoop *mloops; /* faces's vertices */ + const MVert *mverts; /* vertices */ + const MLoopUV *luvs; /* texture coordinates */ + const float (*lnors)[3]; /* loops' normals */ + float (*tangents)[4]; /* output tangents */ + int num_polys; /* number of polygons */ } BKEMeshToTangent; /* Mikktspace's API */ @@ -103,7 +103,7 @@ void BKE_mesh_calc_loop_tangent_single_ex(const MVert *mverts, const int UNUSED(numVerts), const MLoop *mloops, float (*r_looptangent)[4], - float (*loopnors)[3], + const float (*loopnors)[3], const MLoopUV *loopuvs, const int UNUSED(numLoops), const MPoly *mpolys, @@ -155,8 +155,7 @@ void BKE_mesh_calc_loop_tangent_single(Mesh *mesh, float (*r_looptangents)[4], ReportList *reports) { - MLoopUV *loopuvs; - float(*loopnors)[3]; + const MLoopUV *loopuvs; /* Check we have valid texture coordinates first! */ if (uvmap) { @@ -173,7 +172,7 @@ void BKE_mesh_calc_loop_tangent_single(Mesh *mesh, return; } - loopnors = CustomData_get_layer(&mesh->ldata, CD_NORMAL); + const float(*loopnors)[3] = CustomData_get_layer(&mesh->ldata, CD_NORMAL); if (!loopnors) { BKE_report( reports, RPT_ERROR, "Tangent space computation needs loop normals, none found, aborting"); @@ -205,10 +204,10 @@ typedef struct { const float (*precomputedFaceNormals)[3]; const float (*precomputedLoopNormals)[3]; const MLoopTri *looptri; - MLoopUV *mloopuv; /* texture coordinates */ - const MPoly *mpoly; /* indices */ - const MLoop *mloop; /* indices */ - const MVert *mvert; /* vertex coordinates */ + const MLoopUV *mloopuv; /* texture coordinates */ + const MPoly *mpoly; /* indices */ + const MLoop *mloop; /* indices */ + const MVert *mvert; /* vertex coordinates */ const float (*vert_normals)[3]; const float (*orco)[3]; float (*tangent)[4]; /* destination */ diff --git a/source/blender/blenkernel/intern/mesh_tessellate.c b/source/blender/blenkernel/intern/mesh_tessellate.c index ea3cc043267..7cb656d2357 100644 --- a/source/blender/blenkernel/intern/mesh_tessellate.c +++ b/source/blender/blenkernel/intern/mesh_tessellate.c @@ -69,7 +69,7 @@ static void mesh_loops_to_tessdata(CustomData *fdata, for (i = 0; i < numUV; i++) { MTFace *texface = CustomData_get_layer_n(fdata, CD_MTFACE, i); - MLoopUV *mloopuv = CustomData_get_layer_n(ldata, CD_MLOOPUV, i); + const MLoopUV *mloopuv = CustomData_get_layer_n(ldata, CD_MLOOPUV, i); for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces; pidx++, lidx++, findex++, texface++) { @@ -81,7 +81,7 @@ static void mesh_loops_to_tessdata(CustomData *fdata, for (i = 0; i < numCol; i++) { MCol(*mcol)[4] = CustomData_get_layer_n(fdata, CD_MCOL, i); - MLoopCol *mloopcol = CustomData_get_layer_n(ldata, CD_PROP_BYTE_COLOR, i); + const MLoopCol *mloopcol = CustomData_get_layer_n(ldata, CD_PROP_BYTE_COLOR, i); for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, mcol++) { for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { @@ -92,7 +92,7 @@ static void mesh_loops_to_tessdata(CustomData *fdata, if (hasPCol) { MCol(*mcol)[4] = CustomData_get_layer(fdata, CD_PREVIEW_MCOL); - MLoopCol *mloopcol = CustomData_get_layer(ldata, CD_PREVIEW_MLOOPCOL); + const MLoopCol *mloopcol = CustomData_get_layer(ldata, CD_PREVIEW_MLOOPCOL); for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, mcol++) { for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { @@ -103,7 +103,7 @@ static void mesh_loops_to_tessdata(CustomData *fdata, if (hasOrigSpace) { OrigSpaceFace *of = CustomData_get_layer(fdata, CD_ORIGSPACE); - OrigSpaceLoop *lof = CustomData_get_layer(ldata, CD_ORIGSPACE_MLOOP); + const OrigSpaceLoop *lof = CustomData_get_layer(ldata, CD_ORIGSPACE_MLOOP); for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, of++) { for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { @@ -114,7 +114,7 @@ static void mesh_loops_to_tessdata(CustomData *fdata, if (hasLoopNormal) { short(*fnors)[4][3] = CustomData_get_layer(fdata, CD_TESSLOOPNORMAL); - float(*lnors)[3] = CustomData_get_layer(ldata, CD_NORMAL); + const float(*lnors)[3] = CustomData_get_layer(ldata, CD_NORMAL); for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, fnors++) { for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { @@ -126,7 +126,7 @@ static void mesh_loops_to_tessdata(CustomData *fdata, if (hasLoopTangent) { /* Need to do for all UV maps at some point. */ float(*ftangents)[4] = CustomData_get_layer(fdata, CD_TANGENT); - float(*ltangents)[4] = CustomData_get_layer(ldata, CD_TANGENT); + const float(*ltangents)[4] = CustomData_get_layer(ldata, CD_TANGENT); for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces; pidx++, lidx++, findex++) { @@ -154,8 +154,8 @@ int BKE_mesh_tessface_calc_ex(CustomData *fdata, const int looptri_num = poly_to_tri_count(totpoly, totloop); - MPoly *mp, *mpoly; - MLoop *ml, *mloop; + const MPoly *mp, *mpoly; + const MLoop *ml, *mloop; MFace *mface, *mf; MemArena *arena = NULL; int *mface_to_poly_map; diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 5ffd253be3b..6348d83362e 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -1067,7 +1067,7 @@ void BKE_modifier_check_uuids_unique_and_report(const Object *object) BLI_gset_free(used_uuids, NULL); } -void BKE_modifier_blend_write(BlendWriter *writer, ListBase *modbase) +void BKE_modifier_blend_write(BlendWriter *writer, const ID *id_owner, ListBase *modbase) { if (modbase == NULL) { return; @@ -1076,7 +1076,13 @@ void BKE_modifier_blend_write(BlendWriter *writer, ListBase *modbase) LISTBASE_FOREACH (ModifierData *, md, modbase) { const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); if (mti == NULL) { - return; + continue; + } + + /* If the blendWrite callback is defined, it should handle the whole writing process. */ + if (mti->blendWrite != NULL) { + mti->blendWrite(writer, id_owner, md); + continue; } BLO_write_struct_by_name(writer, mti->structName, md); @@ -1162,10 +1168,6 @@ void BKE_modifier_blend_write(BlendWriter *writer, ListBase *modbase) writestruct(wd, DATA, MFace, collmd->numfaces, collmd->mfaces); #endif } - - if (mti->blendWrite != NULL) { - mti->blendWrite(writer, md); - } } } diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index b0e235662cb..63945f9ed42 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -1021,7 +1021,7 @@ void multires_modifier_update_mdisps(struct DerivedMesh *dm, Scene *scene) CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; Object *ob; Mesh *me; - MDisps *mdisps; + const MDisps *mdisps; MultiresModifierData *mmd; ob = ccgdm->multires.ob; @@ -1403,7 +1403,7 @@ static void multires_apply_smat(struct Depsgraph *UNUSED(depsgraph), } } -int multires_mdisp_corners(MDisps *s) +int multires_mdisp_corners(const MDisps *s) { int lvl = 13; diff --git a/source/blender/blenkernel/intern/multires_reshape_util.c b/source/blender/blenkernel/intern/multires_reshape_util.c index aed13adf56d..34aa90aa554 100644 --- a/source/blender/blenkernel/intern/multires_reshape_util.c +++ b/source/blender/blenkernel/intern/multires_reshape_util.c @@ -10,12 +10,14 @@ #include "MEM_guardedalloc.h" #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "BLI_task.h" +#include "BKE_customdata.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_multires.h" diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 200eefb73ec..49a8406d92d 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -1116,7 +1116,7 @@ static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node) PointerRNA ptr; RNA_pointer_create((ID *)ntree, &RNA_Node, node, &ptr); - /* XXX Warning: context can be nullptr in case nodes are added in do_versions. + /* XXX WARNING: context can be nullptr in case nodes are added in do_versions. * Delayed init is not supported for nodes with context-based `initfunc_api` at the moment. */ BLI_assert(C != nullptr); ntype->initfunc_api(C, &ptr); @@ -1165,7 +1165,7 @@ static void node_set_typeinfo(const struct bContext *C, } } -/* Warning: default_value must either be null or match the typeinfo at this point. +/* WARNING: default_value must either be null or match the typeinfo at this point. * This function is called both for initializing new sockets and after loading files. */ static void node_socket_set_typeinfo(bNodeTree *ntree, @@ -4502,6 +4502,8 @@ static void registerCompositNodes() register_node_type_cmp_premulkey(); register_node_type_cmp_separate_xyz(); register_node_type_cmp_combine_xyz(); + register_node_type_cmp_separate_color(); + register_node_type_cmp_combine_color(); register_node_type_cmp_diff_matte(); register_node_type_cmp_distance_matte(); @@ -4574,6 +4576,8 @@ static void registerShaderNodes() register_node_type_sh_vect_transform(); register_node_type_sh_squeeze(); register_node_type_sh_invert(); + register_node_type_sh_sepcolor(); + register_node_type_sh_combcolor(); register_node_type_sh_seprgb(); register_node_type_sh_combrgb(); register_node_type_sh_sephsv(); @@ -4660,6 +4664,8 @@ static void registerTextureNodes() register_node_type_tex_distance(); register_node_type_tex_compose(); register_node_type_tex_decompose(); + register_node_type_tex_combine_color(); + register_node_type_tex_separate_color(); register_node_type_tex_output(); register_node_type_tex_viewer(); @@ -4821,6 +4827,7 @@ static void registerFunctionNodes() { register_node_type_fn_align_euler_to_vector(); register_node_type_fn_boolean_math(); + register_node_type_fn_combine_color(); register_node_type_fn_compare(); register_node_type_fn_float_to_int(); register_node_type_fn_input_bool(); @@ -4832,6 +4839,7 @@ static void registerFunctionNodes() register_node_type_fn_random_value(); register_node_type_fn_replace_string(); register_node_type_fn_rotate_euler(); + register_node_type_fn_separate_color(); register_node_type_fn_slice_string(); register_node_type_fn_string_length(); register_node_type_fn_value_to_string(); diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 2a25d73ed87..55b9951c52d 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -583,7 +583,7 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre } BKE_particle_system_blend_write(writer, &ob->particlesystem); - BKE_modifier_blend_write(writer, &ob->modifiers); + BKE_modifier_blend_write(writer, &ob->id, &ob->modifiers); BKE_gpencil_modifier_blend_write(writer, &ob->greasepencil_modifiers); BKE_shaderfx_blend_write(writer, &ob->shader_fx); @@ -2114,7 +2114,7 @@ static const char *get_obdata_defname(int type) case OB_SPEAKER: return DATA_("Speaker"); case OB_CURVES: - return DATA_("HairCurves"); + return DATA_("Curves"); case OB_POINTCLOUD: return DATA_("PointCloud"); case OB_VOLUME: diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index e7ed100ed03..7c96c463339 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -237,14 +237,14 @@ void BKE_packedfile_pack_all(Main *bmain, ReportList *reports, bool verbose) for (ima = bmain->images.first; ima; ima = ima->id.next) { if (BKE_image_has_packedfile(ima) == false && !ID_IS_LINKED(ima)) { - if (ima->source == IMA_SRC_FILE) { + if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_TILED)) { BKE_image_packfiles(reports, ima, ID_BLEND_PATH(bmain, &ima->id)); tot++; } - else if (BKE_image_has_multiple_ibufs(ima) && verbose) { + else if (ELEM(ima->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE) && verbose) { BKE_reportf(reports, RPT_WARNING, - "Image '%s' skipped, movies, image sequences and packed files not supported", + "Image '%s' skipped, packing movies or image sequences not supported", ima->id.name + 2); } } @@ -494,15 +494,22 @@ static void unpack_generate_paths(const char *name, if (tempname[0] == '\0') { /* NOTE: we generally do not have any real way to re-create extension out of data. */ - BLI_strncpy(tempname, id->name + 2, sizeof(tempname)); + const size_t len = BLI_strncpy_rlen(tempname, id->name + 2, sizeof(tempname)); printf("%s\n", tempname); - /* For images we can add the file extension based on the file magic. */ + /* For images ensure that the temporary filename contains tile number information as well as + * a file extension based on the file magic. */ if (id_type == ID_IM) { - ImagePackedFile *imapf = ((Image *)id)->packedfiles.last; + Image *ima = (Image *)id; + ImagePackedFile *imapf = ima->packedfiles.last; if (imapf != NULL && imapf->packedfile != NULL) { const PackedFile *pf = imapf->packedfile; enum eImbFileType ftype = IMB_ispic_type_from_memory((const uchar *)pf->data, pf->size); + if (ima->source == IMA_SRC_TILED) { + char tile_number[6]; + BLI_snprintf(tile_number, sizeof(tile_number), ".%d", imapf->tile_number); + BLI_strncpy(tempname + len, tile_number, sizeof(tempname) - len); + } if (ftype != IMB_FTYPE_NONE) { const int imtype = BKE_ftype_to_imtype(ftype, NULL); BKE_image_path_ensure_ext_from_imtype(tempname, imtype); @@ -639,6 +646,11 @@ int BKE_packedfile_unpack_image(Main *bmain, /* keep the new name in the image for non-pack specific reasons */ if (how != PF_REMOVE) { BLI_strncpy(ima->filepath, new_file_path, sizeof(imapf->filepath)); + if (ima->source == IMA_SRC_TILED) { + /* Ensure that the Image filepath is kept in a tokenized format. */ + char *filename = (char *)BLI_path_basename(ima->filepath); + BKE_image_ensure_tile_token(filename); + } } MEM_freeN(new_file_path); } diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 0f523d87d9b..cff7eb20b05 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -606,6 +606,11 @@ ePaintMode BKE_paintmode_get_from_tool(const struct bToolRef *tref) Brush *BKE_paint_brush(Paint *p) { + return (Brush *)BKE_paint_brush_for_read((const Paint *)p); +} + +const Brush *BKE_paint_brush_for_read(const Paint *p) +{ return p ? p->brush : NULL; } @@ -2051,7 +2056,7 @@ void BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(Mesh *mesh) void BKE_sculpt_sync_face_sets_visibility_to_base_mesh(Mesh *mesh) { - int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + const int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); if (!face_sets) { return; } @@ -2066,7 +2071,7 @@ void BKE_sculpt_sync_face_sets_visibility_to_base_mesh(Mesh *mesh) void BKE_sculpt_sync_face_sets_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg) { - int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + const int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); if (!face_sets) { return; } diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index b935f2afaaa..a5f7f73af70 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -1667,7 +1667,7 @@ void psys_interpolate_face(Mesh *mesh, const float (*vert_normals)[3], MFace *mface, MTFace *tface, - float (*orcodata)[3], + const float (*orcodata)[3], float w[4], float vec[3], float nor[3], @@ -1680,7 +1680,7 @@ void psys_interpolate_face(Mesh *mesh, float *uv1, *uv2, *uv3, *uv4; float n1[3], n2[3], n3[3], n4[3]; float tuv[4][2]; - float *o1, *o2, *o3, *o4; + const float *o1, *o2, *o3, *o4; v1 = mvert[mface->v1].co; v2 = mvert[mface->v2].co; @@ -1903,9 +1903,10 @@ int psys_particle_dm_face_lookup(Mesh *mesh_final, struct LinkNode **poly_nodes) { MFace *mtessface_final; - OrigSpaceFace *osface_final; + const OrigSpaceFace *osface_final; int pindex_orig; - float uv[2], (*faceuv)[2]; + float uv[2]; + const float(*faceuv)[2]; const int *index_mf_to_mpoly_deformed = NULL; const int *index_mf_to_mpoly = NULL; @@ -2048,11 +2049,7 @@ static int psys_map_index_on_dm(Mesh *mesh, } else { /* FROM_FACE/FROM_VOLUME */ /* find a face on the derived mesh that uses this face */ - MFace *mface; - OrigSpaceFace *osface; - int i; - - i = index_dmcache; + int i = index_dmcache; if (i == DMCACHE_NOTFOUND || i >= mesh->totface) { return 0; @@ -2062,8 +2059,8 @@ static int psys_map_index_on_dm(Mesh *mesh, /* modify the original weights to become * weights for the derived mesh face */ - osface = CustomData_get_layer(&mesh->fdata, CD_ORIGSPACE); - mface = &mesh->mface[i]; + OrigSpaceFace *osface = CustomData_get_layer(&mesh->fdata, CD_ORIGSPACE); + const MFace *mface = &mesh->mface[i]; if (osface == NULL) { mapfw[0] = mapfw[1] = mapfw[2] = mapfw[3] = 0.0f; @@ -2090,7 +2087,7 @@ void psys_particle_on_dm(Mesh *mesh_final, float orco[3]) { float tmpnor[3], mapfw[4]; - float(*orcodata)[3]; + const float(*orcodata)[3]; int mapindex; if (!psys_map_index_on_dm( @@ -3628,7 +3625,7 @@ static void psys_cache_edit_paths_iter(void *__restrict iter_data_v, BKE_defvert_weight_to_rgb(ca->col, pind.hkey[1]->weight); } else { - /* warning: copied from 'do_particle_interpolation' (without 'mvert' array stepping) */ + /* WARNING: copied from 'do_particle_interpolation' (without 'mvert' array stepping) */ float real_t; if (result.time < 0.0f) { real_t = -result.time; @@ -3796,7 +3793,7 @@ void psys_get_from_key(ParticleKey *key, float loc[3], float vel[3], float rot[4 } } -static void triatomat(float *v1, float *v2, float *v3, float (*uv)[2], float mat[4][4]) +static void triatomat(float *v1, float *v2, float *v3, const float (*uv)[2], float mat[4][4]) { float det, w1, w2, d1[2], d2[2]; @@ -3842,8 +3839,7 @@ static void psys_face_mat(Object *ob, Mesh *mesh, ParticleData *pa, float mat[4] { float v[3][3]; MFace *mface; - OrigSpaceFace *osface; - float(*orcodata)[3]; + const float(*orcodata)[3]; int i = (ELEM(pa->num_dmcache, DMCACHE_ISCHILD, DMCACHE_NOTFOUND)) ? pa->num : pa->num_dmcache; if (i == -1 || i >= mesh->totface) { @@ -3852,7 +3848,7 @@ static void psys_face_mat(Object *ob, Mesh *mesh, ParticleData *pa, float mat[4] } mface = &mesh->mface[i]; - osface = CustomData_get(&mesh->fdata, i, CD_ORIGSPACE); + const OrigSpaceFace *osface = CustomData_get(&mesh->fdata, i, CD_ORIGSPACE); if (orco && (orcodata = CustomData_get_layer(&mesh->vdata, CD_ORCO))) { copy_v3_v3(v[0], orcodata[mface->v1]); @@ -4159,7 +4155,7 @@ static int get_particle_uv(Mesh *mesh, bool from_vert) { MFace *mf; - MTFace *tf; + const MTFace *tf; int i; tf = CustomData_get_layer_named(&mesh->fdata, CD_MTFACE, name); @@ -5039,7 +5035,6 @@ void psys_get_dupli_texture(ParticleSystem *psys, float uv[2], float orco[3]) { - MFace *mface; float loc[3]; int num; @@ -5063,9 +5058,9 @@ void psys_get_dupli_texture(ParticleSystem *psys, const int uv_idx = CustomData_get_render_layer(mtf_data, CD_MTFACE); if (uv_idx >= 0) { - MTFace *mtface = CustomData_get_layer_n(mtf_data, CD_MTFACE, uv_idx); + const MTFace *mtface = CustomData_get_layer_n(mtf_data, CD_MTFACE, uv_idx); if (mtface != NULL) { - mface = CustomData_get(&psmd->mesh_final->fdata, cpa->num, CD_MFACE); + const MFace *mface = CustomData_get(&psmd->mesh_final->fdata, cpa->num, CD_MFACE); mtface += cpa->num; psys_interpolate_uvs(mtface, mface->v4, cpa->fuv, uv); } @@ -5107,8 +5102,8 @@ void psys_get_dupli_texture(ParticleSystem *psys, const int uv_idx = CustomData_get_render_layer(mtf_data, CD_MTFACE); if (uv_idx >= 0) { - MTFace *mtface = CustomData_get_layer_n(mtf_data, CD_MTFACE, uv_idx); - mface = CustomData_get(&psmd->mesh_final->fdata, num, CD_MFACE); + const MTFace *mtface = CustomData_get_layer_n(mtf_data, CD_MTFACE, uv_idx); + const MFace *mface = CustomData_get(&psmd->mesh_final->fdata, num, CD_MFACE); mtface += num; psys_interpolate_uvs(mtface, mface->v4, pa->fuv, uv); } diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c index 244396af6e6..4be48efb2b5 100644 --- a/source/blender/blenkernel/intern/particle_distribute.c +++ b/source/blender/blenkernel/intern/particle_distribute.c @@ -808,7 +808,7 @@ static void exec_distribute_child(TaskPool *__restrict UNUSED(pool), void *taskd static int distribute_compare_orig_index(const void *p1, const void *p2, void *user_data) { - int *orig_index = (int *)user_data; + const int *orig_index = (const int *)user_data; int index1 = orig_index[*(const int *)p1]; int index2 = orig_index[*(const int *)p2]; @@ -989,7 +989,7 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, if (from == PART_FROM_VERT) { MVert *mv = mesh->mvert; - float(*orcodata)[3] = CustomData_get_layer(&mesh->vdata, CD_ORCO); + const float(*orcodata)[3] = CustomData_get_layer(&mesh->vdata, CD_ORCO); int totvert = mesh->totvert; tree = BLI_kdtree_3d_new(totvert); @@ -1037,7 +1037,7 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, if ((part->flag & PART_EDISTR || children) && from != PART_FROM_VERT) { MVert *v1, *v2, *v3, *v4; float totarea = 0.0f, co1[3], co2[3], co3[3], co4[3]; - float(*orcodata)[3]; + const float(*orcodata)[3]; orcodata = CustomData_get_layer(&mesh->vdata, CD_ORCO); @@ -1219,7 +1219,7 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, /* For hair, sort by origindex (allows optimization's in rendering), */ /* however with virtual parents the children need to be in random order. */ if (part->type == PART_HAIR && !(part->childtype == PART_CHILD_FACES && part->parents != 0.0f)) { - int *orig_index = NULL; + const int *orig_index = NULL; if (from == PART_FROM_VERT) { if (mesh->totvert) { @@ -1233,8 +1233,11 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, } if (orig_index) { - BLI_qsort_r( - particle_element, totpart, sizeof(int), distribute_compare_orig_index, orig_index); + BLI_qsort_r(particle_element, + totpart, + sizeof(int), + distribute_compare_orig_index, + (void *)orig_index); } } diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 7fdc60a265b..a7ab1536b1f 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -321,8 +321,9 @@ void psys_calc_dmcache(Object *ob, Mesh *mesh_final, Mesh *mesh_original, Partic if (!mesh_final->runtime.deformed_only) { /* Will use later to speed up subsurf/evaluated mesh. */ LinkNode *node, *nodedmelem, **nodearray; - int totdmelem, totelem, i, *origindex, *origindex_poly = NULL; - + int totdmelem, totelem, i; + const int *origindex; + const int *origindex_poly = NULL; if (psys->part->from == PART_FROM_VERT) { totdmelem = mesh_final->totvert; diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index b5480673653..9506a077dc6 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -10,6 +10,11 @@ extern "C" { #endif +struct MLoop; +struct MLoopTri; +struct MPoly; +struct MVert; + /* Axis-aligned bounding box */ typedef struct { float bmin[3], bmax[3]; @@ -141,12 +146,12 @@ struct PBVH { /* Mesh data */ const struct Mesh *mesh; - /* Note: Normals are not const because they can be updated for drawing by sculpt code. */ + /* NOTE: Normals are not `const` because they can be updated for drawing by sculpt code. */ float (*vert_normals)[3]; - MVert *verts; - const MPoly *mpoly; - const MLoop *mloop; - const MLoopTri *looptri; + struct MVert *verts; + const struct MPoly *mpoly; + const struct MLoop *mloop; + const struct MLoopTri *looptri; CustomData *vdata; CustomData *ldata; CustomData *pdata; diff --git a/source/blender/blenkernel/intern/pbvh_pixels.cc b/source/blender/blenkernel/intern/pbvh_pixels.cc index 5623cac44ac..49397797c0d 100644 --- a/source/blender/blenkernel/intern/pbvh_pixels.cc +++ b/source/blender/blenkernel/intern/pbvh_pixels.cc @@ -71,7 +71,7 @@ static void extract_barycentric_pixels(UDIMTilePixels &tile_data, int x; for (x = minx; x < maxx; x++) { - float2 uv(float(x) / image_buffer->x, float(y) / image_buffer->y); + float2 uv((float(x) + 0.5f) / image_buffer->x, (float(y) + 0.5f) / image_buffer->y); float3 barycentric_weights; barycentric_weights_v2(uvs[0], uvs[1], uvs[2], uv, barycentric_weights); @@ -108,7 +108,7 @@ struct EncodePixelsUserData { ImageUser *image_user; PBVH *pbvh; Vector<PBVHNode *> *nodes; - MLoopUV *ldata_uv; + const MLoopUV *ldata_uv; }; static void do_encode_pixels(void *__restrict userdata, @@ -283,7 +283,8 @@ static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image return; } - MLoopUV *ldata_uv = static_cast<MLoopUV *>(CustomData_get_layer(&mesh->ldata, CD_MLOOPUV)); + const MLoopUV *ldata_uv = static_cast<const MLoopUV *>( + CustomData_get_layer(&mesh->ldata, CD_MLOOPUV)); if (ldata_uv == nullptr) { return; } @@ -307,6 +308,12 @@ static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image apply_watertight_check(pbvh, image, image_user); } + /* Rebuild the undo regions. */ + for (PBVHNode *node : nodes_to_update) { + NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data); + node_data->rebuild_undo_regions(); + } + /* Clear the UpdatePixels flag. */ for (PBVHNode *node : nodes_to_update) { node->flag = static_cast<PBVHNodeFlags>(node->flag & ~PBVH_RebuildPixels); diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 353f89068d8..2467ca16670 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -1450,7 +1450,7 @@ static PTCacheFile *ptcache_file_open(PTCacheID *pid, int mode, int cfra) { PTCacheFile *pf; FILE *fp = NULL; - char filename[MAX_PTCACHE_FILE]; + char filepath[MAX_PTCACHE_FILE]; #ifndef DURIAN_POINTCACHE_LIB_OK /* don't allow writing for linked objects */ @@ -1465,20 +1465,20 @@ static PTCacheFile *ptcache_file_open(PTCacheID *pid, int mode, int cfra) } } - ptcache_filename(pid, filename, cfra, true, true); + ptcache_filename(pid, filepath, cfra, true, true); if (mode == PTCACHE_FILE_READ) { - fp = BLI_fopen(filename, "rb"); + fp = BLI_fopen(filepath, "rb"); } else if (mode == PTCACHE_FILE_WRITE) { /* Will create the dir if needs be, same as "//textures" is created. */ - BLI_make_existing_file(filename); + BLI_make_existing_file(filepath); - fp = BLI_fopen(filename, "wb"); + fp = BLI_fopen(filepath, "wb"); } else if (mode == PTCACHE_FILE_UPDATE) { - BLI_make_existing_file(filename); - fp = BLI_fopen(filename, "rb+"); + BLI_make_existing_file(filepath); + fp = BLI_fopen(filepath, "rb+"); } if (!fp) { diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index 7704a74841a..e8c7aff75d1 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -116,15 +116,15 @@ void Spline::reverse() this->mark_cache_invalid(); } -int Spline::evaluated_edges_size() const +int Spline::evaluated_edges_num() const { - const int eval_size = this->evaluated_points_size(); - if (eval_size < 2) { + const int eval_num = this->evaluated_points_num(); + if (eval_num < 2) { /* Two points are required for an edge. */ return 0; } - return this->is_cyclic_ ? eval_size : eval_size - 1; + return this->is_cyclic_ ? eval_num : eval_num - 1; } float Spline::length() const @@ -133,11 +133,11 @@ float Spline::length() const return lengths.is_empty() ? 0.0f : this->evaluated_lengths().last(); } -int Spline::segments_size() const +int Spline::segments_num() const { - const int size = this->size(); + const int num = this->size(); - return is_cyclic_ ? size : size - 1; + return is_cyclic_ ? num : num - 1; } bool Spline::is_cyclic() const @@ -177,7 +177,7 @@ Span<float> Spline::evaluated_lengths() const return evaluated_lengths_cache_; } - const int total = evaluated_edges_size(); + const int total = evaluated_edges_num(); evaluated_lengths_cache_.resize(total); if (total != 0) { Span<float3> positions = this->evaluated_positions(); @@ -242,8 +242,8 @@ Span<float3> Spline::evaluated_tangents() const return evaluated_tangents_cache_; } - const int eval_size = this->evaluated_points_size(); - evaluated_tangents_cache_.resize(eval_size); + const int eval_num = this->evaluated_points_num(); + evaluated_tangents_cache_.resize(eval_num); Span<float3> positions = this->evaluated_positions(); @@ -369,8 +369,8 @@ Span<float3> Spline::evaluated_normals() const return evaluated_normals_cache_; } - const int eval_size = this->evaluated_points_size(); - evaluated_normals_cache_.resize(eval_size); + const int eval_num = this->evaluated_points_num(); + evaluated_normals_cache_.resize(eval_num); Span<float3> tangents = this->evaluated_tangents(); MutableSpan<float3> normals = evaluated_normals_cache_; @@ -410,7 +410,7 @@ Spline::LookupResult Spline::lookup_evaluated_length(const float length) const const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length); const int index = offset - lengths.begin(); - const int next_index = (index == this->evaluated_points_size() - 1) ? 0 : index + 1; + const int next_index = (index == this->evaluated_points_num() - 1) ? 0 : index + 1; const float previous_length = (index == 0) ? 0.0f : lengths[index - 1]; const float length_in_segment = length - previous_length; @@ -420,30 +420,30 @@ Spline::LookupResult Spline::lookup_evaluated_length(const float length) const return LookupResult{index, next_index, factor}; } -Array<float> Spline::sample_uniform_index_factors(const int samples_size) const +Array<float> Spline::sample_uniform_index_factors(const int samples_num) const { const Span<float> lengths = this->evaluated_lengths(); - BLI_assert(samples_size > 0); - Array<float> samples(samples_size); + BLI_assert(samples_num > 0); + Array<float> samples(samples_num); samples[0] = 0.0f; - if (samples_size == 1) { + if (samples_num == 1) { return samples; } const float total_length = this->length(); - const float sample_length = total_length / (samples_size - (is_cyclic_ ? 0 : 1)); + const float sample_length = total_length / (samples_num - (is_cyclic_ ? 0 : 1)); /* Store the length at the previous evaluated point in a variable so it can * start out at zero (the lengths array doesn't contain 0 for the first point). */ float prev_length = 0.0f; int i_sample = 1; - for (const int i_evaluated : IndexRange(this->evaluated_edges_size())) { + for (const int i_evaluated : IndexRange(this->evaluated_edges_num())) { const float length = lengths[i_evaluated]; /* Add every sample that fits in this evaluated edge. */ - while ((sample_length * i_sample) < length && i_sample < samples_size) { + while ((sample_length * i_sample) < length && i_sample < samples_num) { const float factor = (sample_length * i_sample - prev_length) / (length - prev_length); samples[i_sample] = i_evaluated + factor; i_sample++; @@ -454,8 +454,8 @@ Array<float> Spline::sample_uniform_index_factors(const int samples_size) const /* Zero lengths or float inaccuracies can cause invalid values, or simply * skip some, so set the values that weren't completed in the main loop. */ - for (const int i : IndexRange(i_sample, samples_size - i_sample)) { - samples[i] = float(samples_size); + for (const int i : IndexRange(i_sample, samples_num - i_sample)) { + samples[i] = float(samples_num); } if (!is_cyclic_) { @@ -468,23 +468,23 @@ Array<float> Spline::sample_uniform_index_factors(const int samples_size) const Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const { - const int eval_size = this->evaluated_points_size(); + const int eval_num = this->evaluated_points_num(); if (is_cyclic_) { - if (index_factor < eval_size) { + if (index_factor < eval_num) { const int index = std::floor(index_factor); - const int next_index = (index < eval_size - 1) ? index + 1 : 0; + const int next_index = (index < eval_num - 1) ? index + 1 : 0; return LookupResult{index, next_index, index_factor - index}; } - return LookupResult{eval_size - 1, 0, 1.0f}; + return LookupResult{eval_num - 1, 0, 1.0f}; } - if (index_factor < eval_size - 1) { + if (index_factor < eval_num - 1) { const int index = std::floor(index_factor); const int next_index = index + 1; return LookupResult{index, next_index, index_factor - index}; } - return LookupResult{eval_size - 2, eval_size - 1, 1.0f}; + return LookupResult{eval_num - 2, eval_num - 1, 1.0f}; } void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const @@ -504,7 +504,7 @@ void Spline::sample_with_index_factors(const GVArray &src, Span<float> index_factors, GMutableSpan dst) const { - BLI_assert(src.size() == this->evaluated_points_size()); + BLI_assert(src.size() == this->evaluated_points_num()); blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index 8e207f93bf5..80515d0ef37 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -335,7 +335,7 @@ void BezierSpline::mark_cache_invalid() auto_handles_dirty_ = true; } -int BezierSpline::evaluated_points_size() const +int BezierSpline::evaluated_points_num() const { BLI_assert(this->size() > 0); return this->control_point_offsets().last(); @@ -502,12 +502,12 @@ Span<float> BezierSpline::evaluated_mappings() const return evaluated_mapping_cache_; } - const int size = this->size(); - const int eval_size = this->evaluated_points_size(); - evaluated_mapping_cache_.resize(eval_size); + const int num = this->size(); + const int eval_num = this->evaluated_points_num(); + evaluated_mapping_cache_.resize(eval_num); MutableSpan<float> mappings = evaluated_mapping_cache_; - if (eval_size == 1) { + if (eval_num == 1) { mappings.first() = 0.0f; mapping_cache_dirty_ = false; return mappings; @@ -517,7 +517,7 @@ Span<float> BezierSpline::evaluated_mappings() const blender::threading::isolate_task([&]() { /* Isolate the task, since this is function is multi-threaded and holds a lock. */ - calculate_mappings_linear_resolution(offsets, size, resolution_, is_cyclic_, mappings); + calculate_mappings_linear_resolution(offsets, num, resolution_, is_cyclic_, mappings); }); mapping_cache_dirty_ = false; @@ -535,15 +535,15 @@ Span<float3> BezierSpline::evaluated_positions() const return evaluated_position_cache_; } - const int size = this->size(); - const int eval_size = this->evaluated_points_size(); - evaluated_position_cache_.resize(eval_size); + const int num = this->size(); + const int eval_num = this->evaluated_points_num(); + evaluated_position_cache_.resize(eval_num); MutableSpan<float3> positions = evaluated_position_cache_; - if (size == 1) { + if (num == 1) { /* Use a special case for single point splines to avoid checking in #evaluate_segment. */ - BLI_assert(eval_size == 1); + BLI_assert(eval_num == 1); positions.first() = positions_.first(); position_cache_dirty_ = false; return positions; @@ -556,7 +556,7 @@ Span<float3> BezierSpline::evaluated_positions() const const int grain_size = std::max(512 / resolution_, 1); blender::threading::isolate_task([&]() { /* Isolate the task, since this is function is multi-threaded and holds a lock. */ - blender::threading::parallel_for(IndexRange(size - 1), grain_size, [&](IndexRange range) { + blender::threading::parallel_for(IndexRange(num - 1), grain_size, [&](IndexRange range) { for (const int i : range) { this->evaluate_segment(i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i])); } @@ -564,7 +564,7 @@ Span<float3> BezierSpline::evaluated_positions() const }); if (is_cyclic_) { this->evaluate_segment( - size - 1, 0, positions.slice(offsets[size - 1], offsets[size] - offsets[size - 1])); + num - 1, 0, positions.slice(offsets[num - 1], offsets[num] - offsets[num - 1])); } else { /* Since evaluating the bezier segment doesn't add the final point, @@ -579,23 +579,23 @@ Span<float3> BezierSpline::evaluated_positions() const BezierSpline::InterpolationData BezierSpline::interpolation_data_from_index_factor( const float index_factor) const { - const int size = this->size(); + const int num = this->size(); if (is_cyclic_) { - if (index_factor < size) { + if (index_factor < num) { const int index = std::floor(index_factor); - const int next_index = (index < size - 1) ? index + 1 : 0; + const int next_index = (index < num - 1) ? index + 1 : 0; return InterpolationData{index, next_index, index_factor - index}; } - return InterpolationData{size - 1, 0, 1.0f}; + return InterpolationData{num - 1, 0, 1.0f}; } - if (index_factor < size - 1) { + if (index_factor < num - 1) { const int index = std::floor(index_factor); const int next_index = index + 1; return InterpolationData{index, next_index, index_factor - index}; } - return InterpolationData{size - 2, size - 1, 1.0f}; + return InterpolationData{num - 2, num - 1, 1.0f}; } /* Use a spline argument to avoid adding this to the header. */ @@ -605,7 +605,7 @@ static void interpolate_to_evaluated_impl(const BezierSpline &spline, MutableSpan<T> dst) { BLI_assert(src.size() == spline.size()); - BLI_assert(dst.size() == spline.evaluated_points_size()); + BLI_assert(dst.size() == spline.evaluated_points_num()); Span<float> mappings = spline.evaluated_mappings(); for (const int i : dst.index_range()) { @@ -627,8 +627,8 @@ GVArray BezierSpline::interpolate_to_evaluated(const GVArray &src) const return src; } - const int eval_size = this->evaluated_points_size(); - if (eval_size == 1) { + const int eval_num = this->evaluated_points_num(); + if (eval_num == 1) { return src; } @@ -636,7 +636,7 @@ GVArray BezierSpline::interpolate_to_evaluated(const GVArray &src) const blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) { - Array<T> values(eval_size); + Array<T> values(eval_num); interpolate_to_evaluated_impl<T>(*this, src.typed<T>(), values); new_varray = VArray<T>::ForContainer(std::move(values)); } diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc index 9d1d5a53a43..a7eeb82d854 100644 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -124,19 +124,19 @@ void NURBSpline::mark_cache_invalid() length_cache_dirty_ = true; } -int NURBSpline::evaluated_points_size() const +int NURBSpline::evaluated_points_num() const { - if (!this->check_valid_size_and_order()) { + if (!this->check_valid_num_and_order()) { return 0; } - return resolution_ * this->segments_size(); + return resolution_ * this->segments_num(); } void NURBSpline::correct_end_tangents() const { } -bool NURBSpline::check_valid_size_and_order() const +bool NURBSpline::check_valid_num_and_order() const { if (this->size() < order_) { return false; @@ -152,10 +152,10 @@ bool NURBSpline::check_valid_size_and_order() const return true; } -int NURBSpline::knots_size() const +int NURBSpline::knots_num() const { - const int size = this->size() + order_; - return is_cyclic_ ? size + order_ - 1 : size; + const int num = this->size() + order_; + return is_cyclic_ ? num + order_ - 1 : num; } void NURBSpline::calculate_knots() const @@ -173,7 +173,7 @@ void NURBSpline::calculate_knots() const * Covers both Cyclic and EndPoint cases. */ const int tail = is_cyclic_ ? 2 * order - 1 : (is_end_point ? order : 0); - knots_.resize(this->knots_size()); + knots_.resize(this->knots_num()); MutableSpan<float> knots = knots_; int r = head; @@ -203,13 +203,13 @@ void NURBSpline::calculate_knots() const Span<float> NURBSpline::knots() const { if (!knots_dirty_) { - BLI_assert(knots_.size() == this->knots_size()); + BLI_assert(knots_.size() == this->knots_num()); return knots_; } std::lock_guard lock{knots_mutex_}; if (!knots_dirty_) { - BLI_assert(knots_.size() == this->knots_size()); + BLI_assert(knots_.size() == this->knots_num()); return knots_; } @@ -221,7 +221,7 @@ Span<float> NURBSpline::knots() const } static void calculate_basis_for_point(const float parameter, - const int size, + const int num, const int degree, const Span<float> knots, MutableSpan<float> r_weights, @@ -231,7 +231,7 @@ static void calculate_basis_for_point(const float parameter, int start = 0; int end = 0; - for (const int i : IndexRange(size + degree)) { + for (const int i : IndexRange(num + degree)) { const bool knots_equal = knots[i] == knots[i + 1]; if (knots_equal || parameter < knots[i] || parameter > knots[i + 1]) { continue; @@ -248,7 +248,7 @@ static void calculate_basis_for_point(const float parameter, for (const int i_order : IndexRange(2, degree)) { if (end + i_order >= knots.size()) { - end = size + degree - i_order; + end = num + degree - i_order; } for (const int i : IndexRange(end - start + 1)) { const int knot_index = start + i; @@ -284,16 +284,16 @@ const NURBSpline::BasisCache &NURBSpline::calculate_basis_cache() const return basis_cache_; } - const int size = this->size(); - const int eval_size = this->evaluated_points_size(); + const int num = this->size(); + const int eval_num = this->evaluated_points_num(); const int order = this->order(); const int degree = order - 1; - basis_cache_.weights.resize(eval_size * order); - basis_cache_.start_indices.resize(eval_size); + basis_cache_.weights.resize(eval_num * order); + basis_cache_.start_indices.resize(eval_num); - if (eval_size == 0) { + if (eval_num == 0) { return basis_cache_; } @@ -303,14 +303,14 @@ const NURBSpline::BasisCache &NURBSpline::calculate_basis_cache() const const Span<float> control_weights = this->weights(); const Span<float> knots = this->knots(); - const int last_control_point_index = is_cyclic_ ? size + degree : size; + const int last_control_point_index = is_cyclic_ ? num + degree : num; const float start = knots[degree]; const float end = knots[last_control_point_index]; - const float step = (end - start) / this->evaluated_edges_size(); - for (const int i : IndexRange(eval_size)) { + const float step = (end - start) / this->evaluated_edges_num(); + for (const int i : IndexRange(eval_num)) { /* Clamp parameter due to floating point inaccuracy. */ - const float parameter = std::clamp(start + step * i, knots[0], knots[size + degree]); + const float parameter = std::clamp(start + step * i, knots[0], knots[num + degree]); MutableSpan<float> point_weights = basis_weights.slice(i * order, order); @@ -318,7 +318,7 @@ const NURBSpline::BasisCache &NURBSpline::calculate_basis_cache() const parameter, last_control_point_index, degree, knots, point_weights, basis_start_indices[i]); for (const int j : point_weights.index_range()) { - const int point_index = (basis_start_indices[i] + j) % size; + const int point_index = (basis_start_indices[i] + j) % num; point_weights[j] *= control_weights[point_index]; } } @@ -333,7 +333,7 @@ void interpolate_to_evaluated_impl(const NURBSpline::BasisCache &basis_cache, const blender::VArray<T> &src, MutableSpan<T> dst) { - const int size = src.size(); + const int num = src.size(); blender::attribute_math::DefaultMixer<T> mixer(dst); for (const int i : dst.index_range()) { @@ -341,7 +341,7 @@ void interpolate_to_evaluated_impl(const NURBSpline::BasisCache &basis_cache, const int start_index = basis_cache.start_indices[i]; for (const int j : point_weights.index_range()) { - const int point_index = (start_index + j) % size; + const int point_index = (start_index + j) % num; mixer.mix_in(i, src[point_index], point_weights[j]); } } @@ -363,7 +363,7 @@ GVArray NURBSpline::interpolate_to_evaluated(const GVArray &src) const blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) { - Array<T> values(this->evaluated_points_size()); + Array<T> values(this->evaluated_points_num()); interpolate_to_evaluated_impl<T>(basis_cache, this->order(), src.typed<T>(), values); new_varray = VArray<T>::ForContainer(std::move(values)); } @@ -383,8 +383,8 @@ Span<float3> NURBSpline::evaluated_positions() const return evaluated_position_cache_; } - const int eval_size = this->evaluated_points_size(); - evaluated_position_cache_.resize(eval_size); + const int eval_num = this->evaluated_points_num(); + evaluated_position_cache_.resize(eval_num); /* TODO: Avoid copying the evaluated data from the temporary array. */ VArray<float3> evaluated = Spline::interpolate_to_evaluated(positions_.as_span()); diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc index 122f7f6c059..c3cc268c81c 100644 --- a/source/blender/blenkernel/intern/spline_poly.cc +++ b/source/blender/blenkernel/intern/spline_poly.cc @@ -76,7 +76,7 @@ void PolySpline::mark_cache_invalid() length_cache_dirty_ = true; } -int PolySpline::evaluated_points_size() const +int PolySpline::evaluated_points_num() const { return this->size(); } diff --git a/source/blender/blenkernel/intern/subdiv.c b/source/blender/blenkernel/intern/subdiv.c index ee1976d5946..9098c010747 100644 --- a/source/blender/blenkernel/intern/subdiv.c +++ b/source/blender/blenkernel/intern/subdiv.c @@ -25,7 +25,9 @@ #include "opensubdiv_evaluator_capi.h" #include "opensubdiv_topology_refiner_capi.h" -/* =================----====--===== MODULE ==========================------== */ +/* -------------------------------------------------------------------- + * Module. + */ void BKE_subdiv_init() { @@ -37,7 +39,9 @@ void BKE_subdiv_exit() openSubdiv_cleanup(); } -/* ========================== CONVERSION HELPERS ============================ */ +/* -------------------------------------------------------------------- + * Conversion helpers. + */ eSubdivFVarLinearInterpolation BKE_subdiv_fvar_interpolation_from_uv_smooth(int uv_smooth) { @@ -72,7 +76,9 @@ eSubdivVtxBoundaryInterpolation BKE_subdiv_vtx_boundary_interpolation_from_subsu return SUBDIV_VTX_BOUNDARY_EDGE_ONLY; } -/* ================================ SETTINGS ================================ */ +/* -------------------------------------------------------------------- + * Settings. + */ bool BKE_subdiv_settings_equal(const SubdivSettings *settings_a, const SubdivSettings *settings_b) { @@ -83,7 +89,9 @@ bool BKE_subdiv_settings_equal(const SubdivSettings *settings_a, const SubdivSet settings_a->fvar_linear_interpolation == settings_b->fvar_linear_interpolation); } -/* ============================== CONSTRUCTION ============================== */ +/* -------------------------------------------------------------------- + * Construction. + */ /* Creation from scratch. */ @@ -194,7 +202,9 @@ void BKE_subdiv_free(Subdiv *subdiv) MEM_freeN(subdiv); } -/* =========================== PTEX FACES AND GRIDS ========================= */ +/* -------------------------------------------------------------------- + * Topology helpers. + */ int *BKE_subdiv_face_ptex_offset_get(Subdiv *subdiv) { diff --git a/source/blender/blenkernel/intern/subdiv_ccg_mask.c b/source/blender/blenkernel/intern/subdiv_ccg_mask.c index 83f76f85a67..04a274d0215 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg_mask.c +++ b/source/blender/blenkernel/intern/subdiv_ccg_mask.c @@ -155,8 +155,7 @@ static void mask_init_functions(SubdivCCGMaskEvaluator *mask_evaluator) bool BKE_subdiv_ccg_mask_init_from_paint(SubdivCCGMaskEvaluator *mask_evaluator, const struct Mesh *mesh) { - GridPaintMask *grid_paint_mask = CustomData_get_layer(&mesh->ldata, CD_GRID_PAINT_MASK); - if (grid_paint_mask == NULL) { + if (CustomData_get_layer(&mesh->ldata, CD_GRID_PAINT_MASK)) { return false; } /* Allocate all required memory. */ diff --git a/source/blender/blenkernel/intern/subdiv_ccg_material.c b/source/blender/blenkernel/intern/subdiv_ccg_material.c index 9fbc99cb4f1..cf49db15b7b 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg_material.c +++ b/source/blender/blenkernel/intern/subdiv_ccg_material.c @@ -10,6 +10,7 @@ #include "MEM_guardedalloc.h" #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" typedef struct CCGMaterialFromMeshData { const Mesh *mesh; diff --git a/source/blender/blenkernel/intern/subdiv_converter_mesh.c b/source/blender/blenkernel/intern/subdiv_converter_mesh.c index 1c5078df1f3..12a5f00a68b 100644 --- a/source/blender/blenkernel/intern/subdiv_converter_mesh.c +++ b/source/blender/blenkernel/intern/subdiv_converter_mesh.c @@ -295,16 +295,16 @@ static void init_functions(OpenSubdiv_Converter *converter) static void initialize_manifold_index_array(const BLI_bitmap *used_map, const int num_elements, - int **indices_r, - int **indices_reverse_r, - int *num_manifold_elements_r) + int **r_indices, + int **r_indices_reverse, + int *r_num_manifold_elements) { int *indices = NULL; - if (indices_r != NULL) { + if (r_indices != NULL) { indices = MEM_malloc_arrayN(num_elements, sizeof(int), "manifold indices"); } int *indices_reverse = NULL; - if (indices_reverse_r != NULL) { + if (r_indices_reverse != NULL) { indices_reverse = MEM_malloc_arrayN(num_elements, sizeof(int), "manifold indices reverse"); } int offset = 0; @@ -324,13 +324,13 @@ static void initialize_manifold_index_array(const BLI_bitmap *used_map, offset++; } } - if (indices_r != NULL) { - *indices_r = indices; + if (r_indices != NULL) { + *r_indices = indices; } - if (indices_reverse_r != NULL) { - *indices_reverse_r = indices_reverse; + if (r_indices_reverse != NULL) { + *r_indices_reverse = indices_reverse; } - *num_manifold_elements_r = num_elements - offset; + *r_num_manifold_elements = num_elements - offset; } static void initialize_manifold_indices(ConverterStorage *storage) diff --git a/source/blender/blenkernel/intern/subdiv_eval.c b/source/blender/blenkernel/intern/subdiv_eval.c index 9edd9815400..37978d5c2cc 100644 --- a/source/blender/blenkernel/intern/subdiv_eval.c +++ b/source/blender/blenkernel/intern/subdiv_eval.c @@ -23,7 +23,9 @@ #include "opensubdiv_evaluator_capi.h" #include "opensubdiv_topology_refiner_capi.h" -/* ============================ Helper Function ============================ */ +/* -------------------------------------------------------------------- + * Helper functions. + */ static eOpenSubdivEvaluator opensubdiv_evalutor_from_subdiv_evaluator_type( eSubdivEvaluatorType evaluator_type) @@ -40,7 +42,9 @@ static eOpenSubdivEvaluator opensubdiv_evalutor_from_subdiv_evaluator_type( return OPENSUBDIV_EVALUATOR_CPU; } -/* ====================== Main Subdivision Evaluation ====================== */ +/* -------------------------------------------------------------------- + * Main subdivision evaluation. + */ bool BKE_subdiv_eval_begin(Subdiv *subdiv, eSubdivEvaluatorType evaluator_type, @@ -226,7 +230,9 @@ void BKE_subdiv_eval_init_displacement(Subdiv *subdiv) subdiv->displacement_evaluator->initialize(subdiv->displacement_evaluator); } -/* ========================== Single point queries ========================== */ +/* -------------------------------------------------------------------- + * Single point queries. + */ void BKE_subdiv_eval_limit_point( Subdiv *subdiv, const int ptex_face_index, const float u, const float v, float r_P[3]) diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 5e7a0fc116b..ba2df362b92 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -259,7 +259,10 @@ static void get_face_uv_map_vert( } } -static int ss_sync_from_uv(CCGSubSurf *ss, CCGSubSurf *origss, DerivedMesh *dm, MLoopUV *mloopuv) +static int ss_sync_from_uv(CCGSubSurf *ss, + CCGSubSurf *origss, + DerivedMesh *dm, + const MLoopUV *mloopuv) { MPoly *mpoly = dm->getPolyArray(dm); MLoop *mloop = dm->getLoopArray(dm); @@ -381,13 +384,9 @@ static int ss_sync_from_uv(CCGSubSurf *ss, CCGSubSurf *origss, DerivedMesh *dm, static void set_subsurf_legacy_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh *result, int n) { - CCGSubSurf *uvss; - CCGFace **faceMap; - MTFace *tf; - MLoopUV *mluv; CCGFaceIterator fi; int index, gridSize, gridFaces, /*edgeSize,*/ totface, x, y, S; - MLoopUV *dmloopuv = CustomData_get_layer_n(&dm->loopData, CD_MLOOPUV, n); + const MLoopUV *dmloopuv = CustomData_get_layer_n(&dm->loopData, CD_MLOOPUV, n); /* need to update both CD_MTFACE & CD_MLOOPUV, hrmf, we could get away with * just tface except applying the modifier then looses subsurf UV */ MTFace *tface = CustomData_get_layer_n(&result->faceData, CD_MTFACE, n); @@ -398,7 +397,7 @@ static void set_subsurf_legacy_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh * } /* create a CCGSubSurf from uv's */ - uvss = _getSubSurf(NULL, ccgSubSurf_getSubdivisionLevels(ss), 2, CCG_USE_ARENA); + CCGSubSurf *uvss = _getSubSurf(NULL, ccgSubSurf_getSubdivisionLevels(ss), 2, CCG_USE_ARENA); if (!ss_sync_from_uv(uvss, ss, dm, dmloopuv)) { ccgSubSurf_free(uvss); @@ -412,7 +411,7 @@ static void set_subsurf_legacy_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh * gridFaces = gridSize - 1; /* make a map from original faces to CCGFaces */ - faceMap = MEM_mallocN(totface * sizeof(*faceMap), "facemapuv"); + CCGFace **faceMap = MEM_mallocN(totface * sizeof(*faceMap), "facemapuv"); for (ccgSubSurf_initFaceIterator(uvss, &fi); !ccgFaceIterator_isStopped(&fi); ccgFaceIterator_next(&fi)) { CCGFace *f = ccgFaceIterator_getCurrent(&fi); @@ -420,8 +419,8 @@ static void set_subsurf_legacy_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh * } /* load coordinates from uvss into tface */ - tf = tface; - mluv = mloopuv; + MTFace *tf = tface; + MLoopUV *mluv = mloopuv; for (index = 0; index < totface; index++) { CCGFace *f = faceMap[index]; @@ -568,11 +567,8 @@ static void ss_sync_ccg_from_derivedmesh(CCGSubSurf *ss, MEdge *me; MLoop *mloop = dm->getLoopArray(dm), *ml; MPoly *mpoly = dm->getPolyArray(dm), *mp; - // MFace *mf; /* UNUSED */ int totvert = dm->getNumVerts(dm); int totedge = dm->getNumEdges(dm); - // int totface = dm->getNumTessFaces(dm); /* UNUSED */ - // int totpoly = dm->getNumFaces(dm); /* UNUSED */ int i, j; int *index; @@ -773,11 +769,6 @@ static int ccgDM_getNumPolys(DerivedMesh *dm) return ccgSubSurf_getNumFinalFaces(ccgdm->ss); } -static int ccgDM_getNumTessFaces(DerivedMesh *dm) -{ - return dm->numTessFaceData; -} - static int ccgDM_getNumLoops(DerivedMesh *dm) { CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; @@ -883,86 +874,6 @@ static void ccgDM_getFinalVertNo(DerivedMesh *dm, int vertNum, float r_no[3]) copy_v3_v3(r_no, CCG_elem_no(&key, vd)); } -void subsurf_copy_grid_hidden(DerivedMesh *dm, - const MPoly *mpoly, - MVert *mvert, - const MDisps *mdisps) -{ - CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; - CCGSubSurf *ss = ccgdm->ss; - int level = ccgSubSurf_getSubdivisionLevels(ss); - int gridSize = ccgSubSurf_getGridSize(ss); - int edgeSize = ccgSubSurf_getEdgeSize(ss); - int totface = ccgSubSurf_getNumFaces(ss); - int i, j, x, y; - - for (i = 0; i < totface; i++) { - CCGFace *f = ccgdm->faceMap[i].face; - - for (j = 0; j < mpoly[i].totloop; j++) { - const MDisps *md = &mdisps[mpoly[i].loopstart + j]; - int hidden_gridsize = BKE_ccg_gridsize(md->level); - int factor = BKE_ccg_factor(level, md->level); - BLI_bitmap *hidden = md->hidden; - - if (!hidden) { - continue; - } - - for (y = 0; y < gridSize; y++) { - for (x = 0; x < gridSize; x++) { - int vndx, offset; - - vndx = getFaceIndex(ss, f, j, x, y, edgeSize, gridSize); - offset = (y * factor) * hidden_gridsize + (x * factor); - if (BLI_BITMAP_TEST(hidden, offset)) { - mvert[vndx].flag |= ME_HIDE; - } - } - } - } - } -} - -void subsurf_copy_grid_paint_mask(DerivedMesh *dm, - const MPoly *mpoly, - float *paint_mask, - const GridPaintMask *grid_paint_mask) -{ - CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; - CCGSubSurf *ss = ccgdm->ss; - int level = ccgSubSurf_getSubdivisionLevels(ss); - int gridSize = ccgSubSurf_getGridSize(ss); - int edgeSize = ccgSubSurf_getEdgeSize(ss); - int totface = ccgSubSurf_getNumFaces(ss); - int i, j, x, y, factor, gpm_gridsize; - - for (i = 0; i < totface; i++) { - CCGFace *f = ccgdm->faceMap[i].face; - const MPoly *p = &mpoly[i]; - - for (j = 0; j < p->totloop; j++) { - const GridPaintMask *gpm = &grid_paint_mask[p->loopstart + j]; - if (!gpm->data) { - continue; - } - - factor = BKE_ccg_factor(level, gpm->level); - gpm_gridsize = BKE_ccg_gridsize(gpm->level); - - for (y = 0; y < gridSize; y++) { - for (x = 0; x < gridSize; x++) { - int vndx, offset; - - vndx = getFaceIndex(ss, f, j, x, y, edgeSize, gridSize); - offset = y * factor * gpm_gridsize + x * factor; - paint_mask[vndx] = gpm->data[offset]; - } - } - } - } -} - /* utility function */ BLI_INLINE void ccgDM_to_MVert(MVert *mv, const CCGKey *key, CCGElem *elem) { @@ -1336,8 +1247,9 @@ static void *ccgDM_get_vert_data_layer(DerivedMesh *dm, int type) } BLI_rw_mutex_lock(&ccgdm->origindex_cache_rwlock, THREAD_LOCK_WRITE); - DM_add_vert_layer(dm, CD_ORIGINDEX, CD_CALLOC, NULL); - origindex = DM_get_vert_data_layer(dm, CD_ORIGINDEX); + + origindex = CustomData_add_layer( + &dm->vertData, CD_ORIGINDEX, CD_CALLOC, NULL, dm->numVertData); totorig = ccgSubSurf_getNumVerts(ss); totnone = dm->numVertData - totorig; @@ -1375,8 +1287,8 @@ static void *ccgDM_get_edge_data_layer(DerivedMesh *dm, int type) return origindex; } - DM_add_edge_layer(dm, CD_ORIGINDEX, CD_CALLOC, NULL); - origindex = DM_get_edge_data_layer(dm, CD_ORIGINDEX); + origindex = CustomData_add_layer( + &dm->edgeData, CD_ORIGINDEX, CD_CALLOC, NULL, dm->numEdgeData); totedge = ccgSubSurf_getNumEdges(ss); totorig = totedge * (edgeSize - 1); @@ -1418,8 +1330,8 @@ static void *ccgDM_get_poly_data_layer(DerivedMesh *dm, int type) return origindex; } - DM_add_poly_layer(dm, CD_ORIGINDEX, CD_CALLOC, NULL); - origindex = DM_get_poly_data_layer(dm, CD_ORIGINDEX); + origindex = CustomData_add_layer( + &dm->polyData, CD_ORIGINDEX, CD_CALLOC, NULL, dm->numPolyData); totface = ccgSubSurf_getNumFaces(ss); @@ -1592,10 +1504,7 @@ static void set_default_ccgdm_callbacks(CCGDerivedMesh *ccgdm) ccgdm->dm.getNumVerts = ccgDM_getNumVerts; ccgdm->dm.getNumEdges = ccgDM_getNumEdges; ccgdm->dm.getNumLoops = ccgDM_getNumLoops; - /* reuse of ccgDM_getNumTessFaces is intentional here: - * subsurf polys are just created from tessfaces */ ccgdm->dm.getNumPolys = ccgDM_getNumPolys; - ccgdm->dm.getNumTessFaces = ccgDM_getNumTessFaces; ccgdm->dm.getVertCo = ccgDM_getFinalVertCo; ccgdm->dm.getVertNo = ccgDM_getFinalVertNo; @@ -1670,7 +1579,6 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm, int index; int i; int vertNum = 0, edgeNum = 0, faceNum = 0; - int *vertOrigIndex, *polyOrigIndex, *base_polyOrigIndex, *edgeOrigIndex; short *edgeFlags = ccgdm->edgeFlags; DMFlagMat *faceFlags = ccgdm->faceFlags; int *polyidx = NULL; @@ -1687,7 +1595,6 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm, int gridInternalEdges; WeightTable wtable = {NULL}; MEdge *medge = NULL; - MPoly *mpoly = NULL; bool has_edge_cd; edgeSize = ccgSubSurf_getEdgeSize(ss); @@ -1700,13 +1607,13 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm, medge = dm->getEdgeArray(dm); - mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY); - base_polyOrigIndex = CustomData_get_layer(&dm->polyData, CD_ORIGINDEX); + const MPoly *mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY); + const int *base_polyOrigIndex = CustomData_get_layer(&dm->polyData, CD_ORIGINDEX); - vertOrigIndex = DM_get_vert_data_layer(&ccgdm->dm, CD_ORIGINDEX); - edgeOrigIndex = DM_get_edge_data_layer(&ccgdm->dm, CD_ORIGINDEX); + int *vertOrigIndex = DM_get_vert_data_layer(&ccgdm->dm, CD_ORIGINDEX); + int *edgeOrigIndex = DM_get_edge_data_layer(&ccgdm->dm, CD_ORIGINDEX); - polyOrigIndex = DM_get_poly_data_layer(&ccgdm->dm, CD_ORIGINDEX); + int *polyOrigIndex = DM_get_poly_data_layer(&ccgdm->dm, CD_ORIGINDEX); has_edge_cd = ((ccgdm->dm.edgeData.totlayer - (edgeOrigIndex ? 1 : 0)) != 0); @@ -2160,13 +2067,3 @@ void subsurf_calculate_limit_positions(Mesh *me, float (*r_positions)[3]) dm->release(dm); } - -bool subsurf_has_edges(DerivedMesh *dm) -{ - return dm->getNumEdges(dm) != 0; -} - -bool subsurf_has_faces(DerivedMesh *dm) -{ - return dm->getNumPolys(dm) != 0; -} diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index 348d6a91eb8..f9d3a44e5cb 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -60,7 +60,9 @@ static struct { ListBase tracks; } tracking_clipboard; -/*********************** Common functions *************************/ +/* -------------------------------------------------------------------- + * Common functions. + */ /* Free the whole list of tracks, list's head and tail are set to NULL. */ static void tracking_tracks_free(ListBase *tracks) @@ -435,7 +437,9 @@ void BKE_tracking_get_projection_matrix(MovieTracking *tracking, } } -/*********************** clipboard *************************/ +/* -------------------------------------------------------------------- + * Clipboard. + */ void BKE_tracking_clipboard_free(void) { @@ -496,7 +500,9 @@ void BKE_tracking_clipboard_paste_tracks(MovieTracking *tracking, MovieTrackingO } } -/*********************** Tracks *************************/ +/* -------------------------------------------------------------------- + * Tracks. + */ MovieTrackingTrack *BKE_tracking_track_add_empty(MovieTracking *tracking, ListBase *tracks_list) { @@ -721,67 +727,76 @@ bool BKE_tracking_track_has_enabled_marker_at_frame(MovieTrackingTrack *track, i return marker && (marker->flag & MARKER_DISABLED) == 0; } -void BKE_tracking_track_path_clear(MovieTrackingTrack *track, int ref_frame, int action) +static void path_clear_remained(MovieTrackingTrack *track, const int ref_frame) { - int a; - - if (action == TRACK_CLEAR_REMAINED) { - a = 1; - - while (a < track->markersnr) { - if (track->markers[a].framenr > ref_frame) { - track->markersnr = a; - track->markers = MEM_reallocN(track->markers, - sizeof(MovieTrackingMarker) * track->markersnr); - - break; - } - - a++; - } + for (int a = 1; a < track->markersnr; a++) { + if (track->markers[a].framenr > ref_frame) { + track->markersnr = a; + track->markers = MEM_reallocN(track->markers, + sizeof(MovieTrackingMarker) * track->markersnr); - if (track->markersnr) { - tracking_marker_insert_disabled(track, &track->markers[track->markersnr - 1], false, true); + break; } } - else if (action == TRACK_CLEAR_UPTO) { - a = track->markersnr - 1; - while (a >= 0) { - if (track->markers[a].framenr <= ref_frame) { - memmove(track->markers, - track->markers + a, - (track->markersnr - a) * sizeof(MovieTrackingMarker)); + if (track->markersnr) { + tracking_marker_insert_disabled(track, &track->markers[track->markersnr - 1], false, true); + } +} - track->markersnr = track->markersnr - a; - track->markers = MEM_reallocN(track->markers, - sizeof(MovieTrackingMarker) * track->markersnr); +static void path_clear_up_to(MovieTrackingTrack *track, const int ref_frame) +{ + for (int a = track->markersnr - 1; a >= 0; a--) { + if (track->markers[a].framenr <= ref_frame) { + memmove(track->markers, + track->markers + a, + (track->markersnr - a) * sizeof(MovieTrackingMarker)); - break; - } + track->markersnr = track->markersnr - a; + track->markers = MEM_reallocN(track->markers, + sizeof(MovieTrackingMarker) * track->markersnr); - a--; + break; } + } - if (track->markersnr) { - tracking_marker_insert_disabled(track, &track->markers[0], true, true); - } + if (track->markersnr) { + tracking_marker_insert_disabled(track, &track->markers[0], true, true); } - else if (action == TRACK_CLEAR_ALL) { - MovieTrackingMarker *marker, marker_new; +} - marker = BKE_tracking_marker_get(track, ref_frame); - marker_new = *marker; +static void path_clear_all(MovieTrackingTrack *track, const int ref_frame) +{ + MovieTrackingMarker *marker, marker_new; - MEM_freeN(track->markers); - track->markers = NULL; - track->markersnr = 0; + marker = BKE_tracking_marker_get(track, ref_frame); + marker_new = *marker; - BKE_tracking_marker_insert(track, &marker_new); + MEM_freeN(track->markers); + track->markers = NULL; + track->markersnr = 0; - tracking_marker_insert_disabled(track, &marker_new, true, true); - tracking_marker_insert_disabled(track, &marker_new, false, true); - } + BKE_tracking_marker_insert(track, &marker_new); + + tracking_marker_insert_disabled(track, &marker_new, true, true); + tracking_marker_insert_disabled(track, &marker_new, false, true); +} + +void BKE_tracking_track_path_clear(MovieTrackingTrack *track, + const int ref_frame, + const eTrackClearAction action) +{ + switch (action) { + case TRACK_CLEAR_REMAINED: + path_clear_remained(track, ref_frame); + break; + case TRACK_CLEAR_UPTO: + path_clear_up_to(track, ref_frame); + break; + case TRACK_CLEAR_ALL: + path_clear_all(track, ref_frame); + break; + }; } void BKE_tracking_tracks_join(MovieTracking *tracking, @@ -1276,7 +1291,9 @@ void BKE_tracking_tracks_deselect_all(ListBase *tracksbase) } } -/*********************** Marker *************************/ +/* -------------------------------------------------------------------- + * Marker. + */ MovieTrackingMarker *BKE_tracking_marker_insert(MovieTrackingTrack *track, MovieTrackingMarker *marker) @@ -1350,60 +1367,52 @@ void BKE_tracking_marker_delete(MovieTrackingTrack *track, int framenr) } } -void BKE_tracking_marker_clamp(MovieTrackingMarker *marker, int event) +void BKE_tracking_marker_clamp_pattern_position(MovieTrackingMarker *marker) { float pat_min[2], pat_max[2]; - BKE_tracking_marker_pattern_minmax(marker, pat_min, pat_max); - if (event == CLAMP_PAT_DIM) { - for (int a = 0; a < 2; a++) { - /* search shouldn't be resized smaller than pattern */ - marker->search_min[a] = min_ff(pat_min[a], marker->search_min[a]); - marker->search_max[a] = max_ff(pat_max[a], marker->search_max[a]); - } - } - else if (event == CLAMP_PAT_POS) { - float dim[2]; - - sub_v2_v2v2(dim, pat_max, pat_min); - - for (int a = 0; a < 2; a++) { - /* pattern shouldn't be moved outside of search */ - if (pat_min[a] < marker->search_min[a]) { - for (int b = 0; b < 4; b++) { - marker->pattern_corners[b][a] += marker->search_min[a] - pat_min[a]; - } + for (int a = 0; a < 2; a++) { + if (pat_min[a] < marker->search_min[a]) { + for (int b = 0; b < 4; b++) { + marker->pattern_corners[b][a] += marker->search_min[a] - pat_min[a]; } - if (pat_max[a] > marker->search_max[a]) { - for (int b = 0; b < 4; b++) { - marker->pattern_corners[b][a] -= pat_max[a] - marker->search_max[a]; - } + } + if (pat_max[a] > marker->search_max[a]) { + for (int b = 0; b < 4; b++) { + marker->pattern_corners[b][a] -= pat_max[a] - marker->search_max[a]; } } } - else if (event == CLAMP_SEARCH_DIM) { - for (int a = 0; a < 2; a++) { - /* search shouldn't be resized smaller than pattern */ - marker->search_min[a] = min_ff(pat_min[a], marker->search_min[a]); - marker->search_max[a] = max_ff(pat_max[a], marker->search_max[a]); - } +} + +void BKE_tracking_marker_clamp_search_size(MovieTrackingMarker *marker) +{ + float pat_min[2], pat_max[2]; + BKE_tracking_marker_pattern_minmax(marker, pat_min, pat_max); + + for (int a = 0; a < 2; a++) { + marker->search_min[a] = min_ff(pat_min[a], marker->search_min[a]); + marker->search_max[a] = max_ff(pat_max[a], marker->search_max[a]); } - else if (event == CLAMP_SEARCH_POS) { - float dim[2]; +} - sub_v2_v2v2(dim, marker->search_max, marker->search_min); +void BKE_tracking_marker_clamp_search_position(MovieTrackingMarker *marker) +{ + float pat_min[2], pat_max[2]; + BKE_tracking_marker_pattern_minmax(marker, pat_min, pat_max); - for (int a = 0; a < 2; a++) { - /* search shouldn't be moved inside pattern */ - if (marker->search_min[a] > pat_min[a]) { - marker->search_min[a] = pat_min[a]; - marker->search_max[a] = marker->search_min[a] + dim[a]; - } - if (marker->search_max[a] < pat_max[a]) { - marker->search_max[a] = pat_max[a]; - marker->search_min[a] = marker->search_max[a] - dim[a]; - } + float dim[2]; + sub_v2_v2v2(dim, marker->search_max, marker->search_min); + + for (int a = 0; a < 2; a++) { + if (marker->search_min[a] > pat_min[a]) { + marker->search_min[a] = pat_min[a]; + marker->search_max[a] = marker->search_min[a] + dim[a]; + } + if (marker->search_max[a] < pat_max[a]) { + marker->search_max[a] = pat_max[a]; + marker->search_min[a] = marker->search_max[a] - dim[a]; } } } @@ -1591,7 +1600,9 @@ void BKE_tracking_marker_get_subframe_position(MovieTrackingTrack *track, add_v2_v2(pos, track->offset); } -/*********************** Plane Track *************************/ +/* -------------------------------------------------------------------- + * Plane track. + */ MovieTrackingPlaneTrack *BKE_tracking_plane_track_add(MovieTracking *tracking, ListBase *plane_tracks_base, @@ -1796,7 +1807,9 @@ void BKE_tracking_plane_tracks_replace_point_track(MovieTracking *tracking, } } -/*********************** Plane Marker *************************/ +/* -------------------------------------------------------------------- + * Plane marker. + */ MovieTrackingPlaneMarker *BKE_tracking_plane_marker_insert(MovieTrackingPlaneTrack *plane_track, MovieTrackingPlaneMarker *plane_marker) @@ -1974,7 +1987,9 @@ void BKE_tracking_plane_marker_get_subframe_corners(MovieTrackingPlaneTrack *pla } } -/*********************** Object *************************/ +/* -------------------------------------------------------------------- + * Object. + */ MovieTrackingObject *BKE_tracking_object_add(MovieTracking *tracking, const char *name) { @@ -2119,7 +2134,9 @@ MovieTrackingReconstruction *BKE_tracking_object_get_reconstruction(MovieTrackin return &object->reconstruction; } -/*********************** Camera *************************/ +/* -------------------------------------------------------------------- + * Camera. + */ static int reconstructed_camera_index_get(MovieTrackingReconstruction *reconstruction, int framenr, @@ -2275,7 +2292,9 @@ void BKE_tracking_camera_get_reconstructed_interpolate(MovieTracking *tracking, reconstructed_camera_scale_set(object, mat); } -/*********************** Distortion/Undistortion *************************/ +/* -------------------------------------------------------------------- + * (Un)distortion. + */ MovieDistortion *BKE_tracking_distortion_new(MovieTracking *tracking, int calibration_width, @@ -2588,7 +2607,9 @@ void BKE_tracking_max_distortion_delta_across_bound(MovieTracking *tracking, } } -/*********************** Image sampling *************************/ +/* -------------------------------------------------------------------- + * Image sampling. + */ static void disable_imbuf_channels(ImBuf *ibuf, MovieTrackingTrack *track, bool grayscale) { @@ -2832,7 +2853,9 @@ void BKE_tracking_disable_channels( } } -/*********************** Dopesheet functions *************************/ +/* -------------------------------------------------------------------- + * Dopesheet functions. + */ /* ** Channels sort comparators ** */ diff --git a/source/blender/blenkernel/intern/tracking_plane_tracker.c b/source/blender/blenkernel/intern/tracking_plane_tracker.c index 5e60f6f59a9..c4379ea61bc 100644 --- a/source/blender/blenkernel/intern/tracking_plane_tracker.c +++ b/source/blender/blenkernel/intern/tracking_plane_tracker.c @@ -21,12 +21,12 @@ typedef double Vec2[2]; static int point_markers_correspondences_on_both_image( - MovieTrackingPlaneTrack *plane_track, int frame1, int frame2, Vec2 **x1_r, Vec2 **x2_r) + MovieTrackingPlaneTrack *plane_track, int frame1, int frame2, Vec2 **r_x1, Vec2 **r_x2) { Vec2 *x1, *x2; - *x1_r = x1 = MEM_mallocN(sizeof(*x1) * plane_track->point_tracksnr, "point correspondences x1"); - *x2_r = x2 = MEM_mallocN(sizeof(*x1) * plane_track->point_tracksnr, "point correspondences x2"); + *r_x1 = x1 = MEM_mallocN(sizeof(*x1) * plane_track->point_tracksnr, "point correspondences x1"); + *r_x2 = x2 = MEM_mallocN(sizeof(*x1) * plane_track->point_tracksnr, "point correspondences x2"); int correspondence_index = 0; for (int i = 0; i < plane_track->point_tracksnr; i++) { diff --git a/source/blender/blenlib/BLI_any.hh b/source/blender/blenlib/BLI_any.hh index ca3d5756c52..e80dad82d01 100644 --- a/source/blender/blenlib/BLI_any.hh +++ b/source/blender/blenlib/BLI_any.hh @@ -184,7 +184,7 @@ class Any { } /** - * \note: Only needed because the template below does not count as copy assignment operator. + * \note Only needed because the template below does not count as copy assignment operator. */ Any &operator=(const Any &other) { diff --git a/source/blender/blenlib/BLI_bitmap.h b/source/blender/blenlib/BLI_bitmap.h index ca11cd6574e..19d8525311c 100644 --- a/source/blender/blenlib/BLI_bitmap.h +++ b/source/blender/blenlib/BLI_bitmap.h @@ -15,7 +15,7 @@ extern "C" { typedef unsigned int BLI_bitmap; -/* warning: the bitmap does not keep track of its own size or check +/* WARNING: the bitmap does not keep track of its own size or check * for out-of-bounds access */ /* internal use */ diff --git a/source/blender/blenlib/BLI_color_mix.hh b/source/blender/blenlib/BLI_color_mix.hh index 4989ddc609e..322da2bf112 100644 --- a/source/blender/blenlib/BLI_color_mix.hh +++ b/source/blender/blenlib/BLI_color_mix.hh @@ -1042,7 +1042,7 @@ BLI_INLINE Color BLI_mix_colors(const IMB_BlendMode tool, case IMB_BLEND_COLOR: return mix_color<Color, Traits>(a, b, alpha); default: - BLI_assert(0); + BLI_assert_unreachable(); return Color(0, 0, 0, 0); } } diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 3ce2b90e729..063e60ecf03 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -178,7 +178,7 @@ void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries); * Convert given entry's size into human-readable strings. */ void BLI_filelist_entry_size_to_string(const struct stat *st, - uint64_t sz, + uint64_t st_size_fallback, bool compact, char r_size[FILELIST_DIRENTRY_SIZE_LEN]); /** @@ -215,10 +215,10 @@ void BLI_filelist_entry_datetime_to_string(const struct stat *st, /** \name Files * \{ */ -FILE *BLI_fopen(const char *filename, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -void *BLI_gzopen(const char *filename, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -int BLI_open(const char *filename, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -int BLI_access(const char *filename, int mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +FILE *BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +void *BLI_gzopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +int BLI_open(const char *filepath, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +int BLI_access(const char *filepath, int mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); /** * Returns true if the file with the specified name can be written. @@ -226,7 +226,7 @@ int BLI_access(const char *filename, int mode) ATTR_WARN_UNUSED_RESULT ATTR_NONN * to the real UID and GID of the process, not its effective UID and GID. * This shouldn't matter for Blender, which is not going to run privileged anyway. */ -bool BLI_file_is_writable(const char *file) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BLI_file_is_writable(const char *filepath) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); /** * Creates the file with nothing in it, or updates its last-modified date if it already exists. * Returns true if successful (like the unix touch command). @@ -319,7 +319,7 @@ const char *BLI_expand_tilde(const char *path_with_tilde); # define O_BINARY 0 # endif #else -void BLI_get_short_name(char short_name[256], const char *filename); +void BLI_get_short_name(char short_name[256], const char *filepath); #endif /** \} */ diff --git a/source/blender/blenlib/BLI_float3x3.hh b/source/blender/blenlib/BLI_float3x3.hh new file mode 100644 index 00000000000..62478556d9b --- /dev/null +++ b/source/blender/blenlib/BLI_float3x3.hh @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <cmath> +#include <cstdint> + +#include "BLI_assert.h" +#include "BLI_math_base.h" +#include "BLI_math_matrix.h" +#include "BLI_math_vec_types.hh" +#include "BLI_math_vector.h" + +namespace blender { + +struct float3x3 { + /* A 3x3 matrix in column major order. */ + float values[3][3]; + + float3x3() = default; + + float3x3(const float *matrix) + { + memcpy(values, matrix, sizeof(float) * 3 * 3); + } + + float3x3(const float matrix[3][3]) : float3x3(static_cast<const float *>(matrix[0])) + { + } + + static float3x3 zero() + { + float3x3 result; + zero_m3(result.values); + return result; + } + + static float3x3 identity() + { + float3x3 result; + unit_m3(result.values); + return result; + } + + static float3x3 from_translation(const float2 translation) + { + float3x3 result = identity(); + result.values[2][0] = translation.x; + result.values[2][1] = translation.y; + return result; + } + + static float3x3 from_rotation(float rotation) + { + float3x3 result = zero(); + const float cosine = std::cos(rotation); + const float sine = std::sin(rotation); + result.values[0][0] = cosine; + result.values[0][1] = sine; + result.values[1][0] = -sine; + result.values[1][1] = cosine; + result.values[2][2] = 1.0f; + return result; + } + + static float3x3 from_translation_rotation_scale(const float2 translation, + float rotation, + const float2 scale) + { + float3x3 result; + const float cosine = std::cos(rotation); + const float sine = std::sin(rotation); + result.values[0][0] = scale.x * cosine; + result.values[0][1] = scale.x * sine; + result.values[0][2] = 0.0f; + result.values[1][0] = scale.y * -sine; + result.values[1][1] = scale.y * cosine; + result.values[1][2] = 0.0f; + result.values[2][0] = translation.x; + result.values[2][1] = translation.y; + result.values[2][2] = 1.0f; + return result; + } + + static float3x3 from_normalized_axes(const float2 translation, + const float2 horizontal, + const float2 vertical) + { + BLI_ASSERT_UNIT_V2(horizontal); + BLI_ASSERT_UNIT_V2(vertical); + + float3x3 result; + result.values[0][0] = horizontal.x; + result.values[0][1] = horizontal.y; + result.values[0][2] = 0.0f; + result.values[1][0] = vertical.x; + result.values[1][1] = vertical.y; + result.values[1][2] = 0.0f; + result.values[2][0] = translation.x; + result.values[2][1] = translation.y; + result.values[2][2] = 1.0f; + return result; + } + + /* Construct a transformation that is pivoted around the given origin point. So for instance, + * from_origin_transformation(from_rotation(M_PI_2), float2(0.0f, 2.0f)) + * will construct a transformation representing a 90 degree rotation around the point (0, 2). */ + static float3x3 from_origin_transformation(const float3x3 &transformation, const float2 origin) + { + return from_translation(origin) * transformation * from_translation(-origin); + } + + operator float *() + { + return &values[0][0]; + } + + operator const float *() const + { + return &values[0][0]; + } + + float *operator[](const int64_t index) + { + BLI_assert(index >= 0); + BLI_assert(index < 3); + return &values[index][0]; + } + + const float *operator[](const int64_t index) const + { + BLI_assert(index >= 0); + BLI_assert(index < 3); + return &values[index][0]; + } + + using c_style_float3x3 = float[3][3]; + c_style_float3x3 &ptr() + { + return values; + } + + const c_style_float3x3 &ptr() const + { + return values; + } + + friend float3x3 operator*(const float3x3 &a, const float3x3 &b) + { + float3x3 result; + mul_m3_m3m3(result.values, a.values, b.values); + return result; + } + + void operator*=(const float3x3 &other) + { + mul_m3_m3_post(values, other.values); + } + + friend float2 operator*(const float3x3 &transformation, const float2 &vector) + { + float2 result; + mul_v2_m3v2(result, transformation.values, vector); + return result; + } + + friend float2 operator*(const float3x3 &transformation, const float (*vector)[2]) + { + return transformation * float2(vector); + } + + float3x3 transposed() const + { + float3x3 result; + transpose_m3_m3(result.values, values); + return result; + } + + float3x3 inverted() const + { + float3x3 result; + invert_m3_m3(result.values, values); + return result; + } + + friend bool operator==(const float3x3 &a, const float3x3 &b) + { + return equals_m3m3(a.values, b.values); + } +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_length_parameterize.hh b/source/blender/blenlib/BLI_length_parameterize.hh index 6fe1c6513a2..f13641c3a65 100644 --- a/source/blender/blenlib/BLI_length_parameterize.hh +++ b/source/blender/blenlib/BLI_length_parameterize.hh @@ -17,7 +17,7 @@ namespace blender::length_parameterize { * Return the size of the necessary lengths array for a group of points, taking into account the * possible last cyclic segment. * - * \note This is the same as #bke::curves::curve_segment_size. + * \note This is the same as #bke::curves::curve_segment_num. */ inline int lengths_num(const int points_num, const bool cyclic) { diff --git a/source/blender/blenlib/BLI_math_rotation.h b/source/blender/blenlib/BLI_math_rotation.h index 7d10e52f699..192ad482a69 100644 --- a/source/blender/blenlib/BLI_math_rotation.h +++ b/source/blender/blenlib/BLI_math_rotation.h @@ -7,6 +7,7 @@ * \ingroup bli */ +#include "BLI_math_base.h" #include "BLI_utildefines.h" #include "DNA_vec_types.h" diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h index 0344622e81d..15926e8f2d2 100644 --- a/source/blender/blenlib/BLI_string.h +++ b/source/blender/blenlib/BLI_string.h @@ -307,7 +307,7 @@ void BLI_str_format_byte_unit(char dst[15], long long int bytes, bool base_10) A * * Length of 7 is the maximum of the resulting string, for example, `-15.5K\0`. */ -void BLI_str_format_attribute_domain_size(char dst[7], int number_to_format) ATTR_NONNULL(); +void BLI_str_format_decimal_unit(char dst[7], int number_to_format) ATTR_NONNULL(); /** * Compare two strings without regard to case. * diff --git a/source/blender/blenlib/BLI_task.hh b/source/blender/blenlib/BLI_task.hh index d87a86ce696..904dea66f7a 100644 --- a/source/blender/blenlib/BLI_task.hh +++ b/source/blender/blenlib/BLI_task.hh @@ -77,17 +77,19 @@ Value parallel_reduce(IndexRange range, const Reduction &reduction) { #ifdef WITH_TBB - return tbb::parallel_reduce( - tbb::blocked_range<int64_t>(range.first(), range.one_after_last(), grain_size), - identity, - [&](const tbb::blocked_range<int64_t> &subrange, const Value &ident) { - return function(IndexRange(subrange.begin(), subrange.size()), ident); - }, - reduction); + if (range.size() >= grain_size) { + return tbb::parallel_reduce( + tbb::blocked_range<int64_t>(range.first(), range.one_after_last(), grain_size), + identity, + [&](const tbb::blocked_range<int64_t> &subrange, const Value &ident) { + return function(IndexRange(subrange.begin(), subrange.size()), ident); + }, + reduction); + } #else UNUSED_VARS(grain_size, reduction); - return function(range, identity); #endif + return function(range, identity); } /** diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index 7aa221f62ce..6efd1d6d769 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -463,7 +463,7 @@ template<typename T, typename GetFunc> class VArrayImpl_For_Func final : public }; /** - * \note: This is `final` so that #may_have_ownership can be implemented reliably. + * \note This is `final` so that #may_have_ownership can be implemented reliably. */ template<typename StructT, typename ElemT, @@ -725,7 +725,7 @@ template<typename T> class VArrayCommon { /** * Get the element at a specific index. - * \note: This can't return a reference because the value may be computed on the fly. This also + * \note This can't return a reference because the value may be computed on the fly. This also * implies that one can not use this method for assignments. */ T operator[](const int64_t index) const diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index e0f28522d6c..109230ebfa7 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -79,8 +79,8 @@ set(SRC intern/kdtree_3d.c intern/kdtree_4d.c intern/lasso_2d.c - intern/listbase.c intern/length_parameterize.cc + intern/listbase.c intern/math_base.c intern/math_base_inline.c intern/math_base_safe_inline.c @@ -199,6 +199,7 @@ set(SRC BLI_fileops.hh BLI_fileops_types.h BLI_filereader.h + BLI_float3x3.hh BLI_float4x4.hh BLI_fnmatch.h BLI_function_ref.hh @@ -274,8 +275,8 @@ set(SRC BLI_multi_value_map.hh BLI_noise.h BLI_noise.hh - BLI_path_util.h BLI_parameter_pack_utils.hh + BLI_path_util.h BLI_polyfill_2d.h BLI_polyfill_2d_beautify.h BLI_probing_strategies.hh @@ -431,6 +432,7 @@ if(WITH_GTESTS) tests/BLI_edgehash_test.cc tests/BLI_expr_pylike_eval_test.cc tests/BLI_fileops_test.cc + tests/BLI_float3x3_test.cc tests/BLI_function_ref_test.cc tests/BLI_generic_array_test.cc tests/BLI_generic_span_test.cc diff --git a/source/blender/blenlib/intern/BLI_assert.c b/source/blender/blenlib/intern/BLI_assert.c index 2ebeba43dbe..96c16b47214 100644 --- a/source/blender/blenlib/intern/BLI_assert.c +++ b/source/blender/blenlib/intern/BLI_assert.c @@ -40,7 +40,7 @@ void _BLI_assert_abort(void) /* Wrap to remove 'noreturn' attribute since this suppresses missing return statements, * allowing changes to debug builds to accidentally to break release builds. * - * For example `BLI_assert(0);` at the end of a function that returns a value, + * For example `BLI_assert_unreachable();` at the end of a function that returns a value, * will hide that it's missing a return. */ abort(); diff --git a/source/blender/blenlib/intern/BLI_filelist.c b/source/blender/blenlib/intern/BLI_filelist.c index 76fc5b6342a..c6178ebb3a0 100644 --- a/source/blender/blenlib/intern/BLI_filelist.c +++ b/source/blender/blenlib/intern/BLI_filelist.c @@ -237,7 +237,7 @@ unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_ } void BLI_filelist_entry_size_to_string(const struct stat *st, - const uint64_t sz, + const uint64_t st_size_fallback, /* Used to change MB -> M, etc. - is that really useful? */ const bool UNUSED(compact), char r_size[FILELIST_DIRENTRY_SIZE_LEN]) @@ -247,7 +247,7 @@ void BLI_filelist_entry_size_to_string(const struct stat *st, * will buy us some time until files get bigger than 4GB or until * everyone starts using __USE_FILE_OFFSET64 or equivalent. */ - double size = (double)(st ? st->st_size : sz); + double size = (double)(st ? st->st_size : st_size_fallback); #ifdef WIN32 BLI_str_format_byte_unit(r_size, size, false); #else diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c index 57e05233efa..e6ff5bab8a1 100644 --- a/source/blender/blenlib/intern/BLI_ghash.c +++ b/source/blender/blenlib/intern/BLI_ghash.c @@ -173,7 +173,7 @@ BLI_INLINE uint ghash_find_next_bucket_index(const GHash *gh, uint curr_bucket) return curr_bucket; } } - BLI_assert(0); + BLI_assert_unreachable(); return 0; } diff --git a/source/blender/blenlib/intern/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c index 0f52c84c45e..62bf17bd415 100644 --- a/source/blender/blenlib/intern/BLI_kdopbvh.c +++ b/source/blender/blenlib/intern/BLI_kdopbvh.c @@ -893,7 +893,7 @@ BVHTree *BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis) } else { /* should never happen! */ - BLI_assert(0); + BLI_assert_unreachable(); goto fail; } diff --git a/source/blender/blenlib/intern/array_utils.c b/source/blender/blenlib/intern/array_utils.c index f1f1dd60ddf..a401059755d 100644 --- a/source/blender/blenlib/intern/array_utils.c +++ b/source/blender/blenlib/intern/array_utils.c @@ -53,7 +53,7 @@ void _bli_array_wrap(void *arr_v, uint arr_len, size_t arr_stride, int dir) memcpy(arr, buf, arr_stride); } else { - BLI_assert(0); + BLI_assert_unreachable(); } } diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 5ca6fe2efd0..3abd482d6b3 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -164,10 +164,10 @@ bool BLI_file_magic_is_zstd(const char header[4]) return false; } -bool BLI_file_is_writable(const char *filename) +bool BLI_file_is_writable(const char *filepath) { bool writable; - if (BLI_access(filename, W_OK) == 0) { + if (BLI_access(filepath, W_OK) == 0) { /* file exists and I can write to it */ writable = true; } @@ -178,7 +178,7 @@ bool BLI_file_is_writable(const char *filename) else { /* file doesn't exist -- check I can create it in parent directory */ char parent[FILE_MAX]; - BLI_split_dirfile(filename, parent, NULL, sizeof(parent), 0); + BLI_split_dirfile(filepath, parent, NULL, sizeof(parent), 0); #ifdef WIN32 /* windows does not have X_OK */ writable = BLI_access(parent, W_OK) == 0; @@ -224,38 +224,38 @@ static void callLocalErrorCallBack(const char *err) printf("%s\n", err); } -FILE *BLI_fopen(const char *filename, const char *mode) +FILE *BLI_fopen(const char *filepath, const char *mode) { - BLI_assert(!BLI_path_is_rel(filename)); + BLI_assert(!BLI_path_is_rel(filepath)); - return ufopen(filename, mode); + return ufopen(filepath, mode); } -void BLI_get_short_name(char short_name[256], const char *filename) +void BLI_get_short_name(char short_name[256], const char *filepath) { wchar_t short_name_16[256]; int i = 0; - UTF16_ENCODE(filename); + UTF16_ENCODE(filepath); - GetShortPathNameW(filename_16, short_name_16, 256); + GetShortPathNameW(filepath_16, short_name_16, 256); for (i = 0; i < 256; i++) { short_name[i] = (char)short_name_16[i]; } - UTF16_UN_ENCODE(filename); + UTF16_UN_ENCODE(filepath); } -void *BLI_gzopen(const char *filename, const char *mode) +void *BLI_gzopen(const char *filepath, const char *mode) { gzFile gzfile; - BLI_assert(!BLI_path_is_rel(filename)); + BLI_assert(!BLI_path_is_rel(filepath)); /* XXX: Creates file before transcribing the path. */ if (mode[0] == 'w') { - FILE *file = ufopen(filename, "a"); + FILE *file = ufopen(filepath, "a"); if (file == NULL) { /* File couldn't be opened, e.g. due to permission error. */ return NULL; @@ -266,15 +266,15 @@ void *BLI_gzopen(const char *filename, const char *mode) /* temporary #if until we update all libraries to 1.2.7 * for correct wide char path handling */ # if ZLIB_VERNUM >= 0x1270 - UTF16_ENCODE(filename); + UTF16_ENCODE(filepath); - gzfile = gzopen_w(filename_16, mode); + gzfile = gzopen_w(filepath_16, mode); - UTF16_UN_ENCODE(filename); + UTF16_UN_ENCODE(filepath); # else { char short_name[256]; - BLI_get_short_name(short_name, filename); + BLI_get_short_name(short_name, filepath); gzfile = gzopen(short_name, mode); } # endif @@ -282,18 +282,18 @@ void *BLI_gzopen(const char *filename, const char *mode) return gzfile; } -int BLI_open(const char *filename, int oflag, int pmode) +int BLI_open(const char *filepath, int oflag, int pmode) { - BLI_assert(!BLI_path_is_rel(filename)); + BLI_assert(!BLI_path_is_rel(filepath)); - return uopen(filename, oflag, pmode); + return uopen(filepath, oflag, pmode); } -int BLI_access(const char *filename, int mode) +int BLI_access(const char *filepath, int mode) { - BLI_assert(!BLI_path_is_rel(filename)); + BLI_assert(!BLI_path_is_rel(filepath)); - return uaccess(filename, mode); + return uaccess(filepath, mode); } static bool delete_soft(const wchar_t *path_16, const char **error_message) @@ -466,8 +466,8 @@ int BLI_move(const char *file, const char *to) int err; /* windows doesn't support moving to a directory - * it has to be 'mv filename filename' and not - * 'mv filename destination_directory' */ + * it has to be 'mv filepath filepath' and not + * 'mv filepath destination_directory' */ BLI_strncpy(str, to, sizeof(str)); /* points 'to' to a directory ? */ @@ -498,8 +498,8 @@ int BLI_copy(const char *file, const char *to) int err; /* windows doesn't support copying to a directory - * it has to be 'cp filename filename' and not - * 'cp filename destdir' */ + * it has to be 'cp filepath filepath' and not + * 'cp filepath destdir' */ BLI_strncpy(str, to, sizeof(str)); /* points 'to' to a directory ? */ @@ -587,7 +587,7 @@ int BLI_rename(const char *from, const char *to) return 0; } - /* make sure the filenames are different (case insensitive) before removing */ + /* Make sure `from` & `to` are different (case insensitive) before removing. */ if (BLI_exists(to) && BLI_strcasecmp(from, to)) { if (BLI_delete(to, false, false)) { return 1; @@ -728,9 +728,9 @@ static int recursive_operation(const char *startfrom, # ifdef __HAIKU__ { struct stat st_dir; - char filename[FILE_MAX]; - BLI_path_join(filename, sizeof(filename), startfrom, dirent->d_name, NULL); - lstat(filename, &st_dir); + char filepath[FILE_MAX]; + BLI_path_join(filepath, sizeof(filepath), startfrom, dirent->d_name, NULL); + lstat(filepath, &st_dir); is_dir = S_ISDIR(st_dir.st_mode); } # else @@ -903,32 +903,32 @@ static int delete_soft(const char *file, const char **error_message) } # endif -FILE *BLI_fopen(const char *filename, const char *mode) +FILE *BLI_fopen(const char *filepath, const char *mode) { - BLI_assert(!BLI_path_is_rel(filename)); + BLI_assert(!BLI_path_is_rel(filepath)); - return fopen(filename, mode); + return fopen(filepath, mode); } -void *BLI_gzopen(const char *filename, const char *mode) +void *BLI_gzopen(const char *filepath, const char *mode) { - BLI_assert(!BLI_path_is_rel(filename)); + BLI_assert(!BLI_path_is_rel(filepath)); - return gzopen(filename, mode); + return gzopen(filepath, mode); } -int BLI_open(const char *filename, int oflag, int pmode) +int BLI_open(const char *filepath, int oflag, int pmode) { - BLI_assert(!BLI_path_is_rel(filename)); + BLI_assert(!BLI_path_is_rel(filepath)); - return open(filename, oflag, pmode); + return open(filepath, oflag, pmode); } -int BLI_access(const char *filename, int mode) +int BLI_access(const char *filepath, int mode) { - BLI_assert(!BLI_path_is_rel(filename)); + BLI_assert(!BLI_path_is_rel(filepath)); - return access(filename, mode); + return access(filepath, mode); } int BLI_delete(const char *file, bool dir, bool recursive) diff --git a/source/blender/blenlib/intern/hash_md5.c b/source/blender/blenlib/intern/hash_md5.c index cc59662b6de..9da8c0a0941 100644 --- a/source/blender/blenlib/intern/hash_md5.c +++ b/source/blender/blenlib/intern/hash_md5.c @@ -271,7 +271,7 @@ static void *md5_read_ctx(const struct md5_ctx *ctx, void *resbuf) int BLI_hash_md5_stream(FILE *stream, void *resblock) { -#define BLOCKSIZE 4096 /* Important: must be a multiple of 64. */ +#define BLOCKSIZE 4096 /* IMPORTANT: must be a multiple of 64. */ struct md5_ctx ctx; md5_uint32 len[2]; char buffer[BLOCKSIZE + 72]; diff --git a/source/blender/blenlib/intern/listbase.c b/source/blender/blenlib/intern/listbase.c index e2044955e48..3932e5eb051 100644 --- a/source/blender/blenlib/intern/listbase.c +++ b/source/blender/blenlib/intern/listbase.c @@ -193,9 +193,19 @@ void BLI_listbases_swaplinks(ListBase *listbasea, ListBase *listbaseb, void *vli return; } + /* The reference to `linkc` assigns NULL, not a dangling pointer so it can be ignored. */ +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 1201 /* gcc12.1+ only */ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdangling-pointer" +#endif + /* Temporary link to use as placeholder of the links positions */ BLI_insertlinkafter(listbasea, linka, &linkc); +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 1201 /* gcc12.1+ only */ +# pragma GCC diagnostic pop +#endif + /* Bring linka into linkb position */ BLI_remlink(listbasea, linka); BLI_insertlinkafter(listbaseb, linkb, linka); diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c index aeeaf47d813..52cbda82268 100644 --- a/source/blender/blenlib/intern/math_color.c +++ b/source/blender/blenlib/intern/math_color.c @@ -166,7 +166,7 @@ void ycc_to_rgb(float y, float cb, float cr, float *r_r, float *r_g, float *r_b, b = y + 1.772f * cb - 226.816f; break; default: - BLI_assert(0); + BLI_assert_unreachable(); break; } *r_r = r / 255.0f; @@ -238,7 +238,7 @@ void rgb_to_hsl(float r, float g, float b, float *r_h, float *r_s, float *r_l) { const float cmax = max_fff(r, g, b); const float cmin = min_fff(r, g, b); - float h, s, l = min_ff(1.0, (cmax + cmin) / 2.0f); + float h, s, l = min_ff(1.0f, (cmax + cmin) / 2.0f); if (cmax == cmin) { h = s = 0.0f; /* achromatic */ diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index f295734706f..ce9abc36cad 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -1496,7 +1496,7 @@ void orthogonalize_m3(float R[3][3], int axis) } break; default: - BLI_assert(0); + BLI_assert_unreachable(); break; } mul_v3_fl(R[0], size[0]); @@ -1580,7 +1580,7 @@ void orthogonalize_m4(float R[4][4], int axis) } break; default: - BLI_assert(0); + BLI_assert_unreachable(); break; } mul_v3_fl(R[0], size[0]); @@ -1654,7 +1654,7 @@ void orthogonalize_m3_stable(float R[3][3], int axis, bool normalize) orthogonalize_stable(R[2], R[0], R[1], normalize); break; default: - BLI_assert(0); + BLI_assert_unreachable(); break; } } @@ -1672,7 +1672,7 @@ void orthogonalize_m4_stable(float R[4][4], int axis, bool normalize) orthogonalize_stable(R[2], R[0], R[1], normalize); break; default: - BLI_assert(0); + BLI_assert_unreachable(); break; } } @@ -1734,7 +1734,7 @@ static bool orthogonalize_m3_zero_axes_impl(float *mat[3], const float unit_leng break; } default: { - BLI_assert(0); /* Unreachable! */ + BLI_assert_unreachable(); } } @@ -2338,7 +2338,7 @@ void rotate_m4(float mat[4][4], const char axis, const float angle) } break; default: - BLI_assert(0); + BLI_assert_unreachable(); break; } } diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c index 4cd377b109e..92223bdf1d5 100644 --- a/source/blender/blenlib/intern/math_rotation.c +++ b/source/blender/blenlib/intern/math_rotation.c @@ -1143,7 +1143,7 @@ void axis_angle_to_mat3_single(float R[3][3], const char axis, const float angle R[2][2] = 1.0f; break; default: - BLI_assert(0); + BLI_assert_unreachable(); break; } } diff --git a/source/blender/blenlib/intern/scanfill.c b/source/blender/blenlib/intern/scanfill.c index bc07669687e..92fd7f5937b 100644 --- a/source/blender/blenlib/intern/scanfill.c +++ b/source/blender/blenlib/intern/scanfill.c @@ -871,7 +871,7 @@ unsigned int BLI_scanfill_calc_ex(ScanFillContext *sf_ctx, const int flag, const /* Similar code used elsewhere, but this checks for double ups * which historically this function supports so better not change */ - /* warning: this only gives stable direction with single polygons, + /* WARNING: this only gives stable direction with single polygons, * ideally we'd calculate connectivity and each polys normal, see T41047 */ const float *v_prev; diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 8387eb5f4f9..976b9a5cd02 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -1155,7 +1155,7 @@ void BLI_str_format_byte_unit(char dst[15], long long int bytes, const bool base BLI_strncpy(dst + len, base_10 ? units_base_10[order] : units_base_2[order], dst_len - len); } -void BLI_str_format_attribute_domain_size(char dst[7], int number_to_format) +void BLI_str_format_decimal_unit(char dst[7], int number_to_format) { float number_to_format_converted = number_to_format; int order = 0; diff --git a/source/blender/blenlib/intern/string_cursor_utf8.c b/source/blender/blenlib/intern/string_cursor_utf8.c index a6e68401368..7a23b4bb4ad 100644 --- a/source/blender/blenlib/intern/string_cursor_utf8.c +++ b/source/blender/blenlib/intern/string_cursor_utf8.c @@ -198,7 +198,7 @@ void BLI_str_cursor_step_utf8(const char *str, } } else { - BLI_assert(0); + BLI_assert_unreachable(); } } @@ -296,6 +296,6 @@ void BLI_str_cursor_step_utf32(const char32_t *str, } } else { - BLI_assert(0); + BLI_assert_unreachable(); } } diff --git a/source/blender/blenlib/intern/threads.cc b/source/blender/blenlib/intern/threads.cc index 70c1e701348..37fccf6f4fe 100644 --- a/source/blender/blenlib/intern/threads.cc +++ b/source/blender/blenlib/intern/threads.cc @@ -348,7 +348,7 @@ static ThreadMutex *global_mutex_from_type(const int type) case LOCK_VIEW3D: return &_view3d_lock; default: - BLI_assert(0); + BLI_assert_unreachable(); return nullptr; } } diff --git a/source/blender/blenlib/tests/BLI_array_store_test.cc b/source/blender/blenlib/tests/BLI_array_store_test.cc index aa7291e1b41..20e2a4d88f8 100644 --- a/source/blender/blenlib/tests/BLI_array_store_test.cc +++ b/source/blender/blenlib/tests/BLI_array_store_test.cc @@ -644,7 +644,7 @@ static void testbuffer_list_state_random_data(ListBase *lb, break; } default: - BLI_assert(0); + BLI_assert_unreachable(); } } } diff --git a/source/blender/blenlib/tests/BLI_float3x3_test.cc b/source/blender/blenlib/tests/BLI_float3x3_test.cc new file mode 100644 index 00000000000..d22993ee69e --- /dev/null +++ b/source/blender/blenlib/tests/BLI_float3x3_test.cc @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "testing/testing.h" + +#include "BLI_float3x3.hh" +#include "BLI_math_base.h" +#include "BLI_math_vec_types.hh" + +namespace blender::tests { + +TEST(float3x3, Identity) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::identity(); + float2 result = transformation * point; + EXPECT_EQ(result, point); +} + +TEST(float3x3, Translation) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::from_translation(float2(5.0f, 3.0f)); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], 6.0f); + EXPECT_FLOAT_EQ(result[1], 5.0f); +} + +TEST(float3x3, Rotation) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::from_rotation(M_PI_2); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], -2.0f); + EXPECT_FLOAT_EQ(result[1], 1.0f); +} + +TEST(float3x3, TranslationRotationScale) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::from_translation_rotation_scale( + float2(1.0f, 3.0f), M_PI_2, float2(2.0f, 3.0f)); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], -5.0f); + EXPECT_FLOAT_EQ(result[1], 5.0f); +} + +TEST(float3x3, NormalizedAxes) +{ + float2 point(1.0f, 2.0f); + + /* The horizontal is aligned with (1, 1) and vertical is aligned with (-1, 1), in other words, a + * Pi / 4 rotation. */ + float value = std::sqrt(2.0f) / 2.0f; + float3x3 transformation = float3x3::from_normalized_axes( + float2(1.0f, 3.0f), float2(value), float2(-value, value)); + float2 result = transformation * point; + + float3x3 expected_transformation = float3x3::from_translation_rotation_scale( + float2(1.0f, 3.0f), M_PI_4, float2(1.0f)); + float2 expected = expected_transformation * point; + + EXPECT_FLOAT_EQ(result[0], expected[0]); + EXPECT_FLOAT_EQ(result[1], expected[1]); +} + +TEST(float3x3, PostTransformationMultiplication) +{ + float2 point(1.0f, 2.0f); + float3x3 translation = float3x3::from_translation(float2(5.0f, 3.0f)); + float3x3 rotation = float3x3::from_rotation(M_PI_2); + float3x3 transformation = translation * rotation; + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], 3.0f); + EXPECT_FLOAT_EQ(result[1], 4.0f); +} + +TEST(float3x3, PreTransformationMultiplication) +{ + float2 point(1.0f, 2.0f); + float3x3 translation = float3x3::from_translation(float2(5.0f, 3.0f)); + float3x3 rotation = float3x3::from_rotation(M_PI_2); + float3x3 transformation = rotation * translation; + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], -5.0f); + EXPECT_FLOAT_EQ(result[1], 6.0f); +} + +TEST(float3x3, TransformationMultiplicationAssignment) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::from_translation(float2(5.0f, 3.0f)); + transformation *= float3x3::from_rotation(M_PI_2); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], 3.0f); + EXPECT_FLOAT_EQ(result[1], 4.0f); +} + +TEST(float3x3, Inverted) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::from_translation_rotation_scale( + float2(1.0f, 3.0f), M_PI_4, float2(1.0f)); + transformation *= transformation.inverted(); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], 1.0f); + EXPECT_FLOAT_EQ(result[1], 2.0f); +} + +TEST(float3x3, Origin) +{ + float2 point(1.0f, 2.0f); + float3x3 rotation = float3x3::from_rotation(M_PI_2); + float3x3 transformation = float3x3::from_origin_transformation(rotation, float2(0.0f, 2.0f)); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], 0.0f); + EXPECT_FLOAT_EQ(result[1], 3.0f); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_path_util_test.cc b/source/blender/blenlib/tests/BLI_path_util_test.cc index bfd297214c0..4f6f4a5c413 100644 --- a/source/blender/blenlib/tests/BLI_path_util_test.cc +++ b/source/blender/blenlib/tests/BLI_path_util_test.cc @@ -663,7 +663,7 @@ TEST(path_util, PathContains) EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path/inside")) << "A path contains its subdirectory"; EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path/../path/inside")) - << "Paths should be normalised"; + << "Paths should be normalized"; EXPECT_TRUE(BLI_path_contains("C:\\some\\path", "C:\\some\\path\\inside")) << "Windows paths should be supported as well"; @@ -672,7 +672,7 @@ TEST(path_util, PathContains) EXPECT_FALSE(BLI_path_contains("/some/path", "/")) << "Root directory not be contained in a subdirectory"; EXPECT_FALSE(BLI_path_contains("/some/path", "/some/path/../outside")) - << "Paths should be normalised"; + << "Paths should be normalized"; EXPECT_FALSE(BLI_path_contains("/some/path", "/some/path_library")) << "Just sharing a suffix is not enough, path semantics should be followed"; EXPECT_FALSE(BLI_path_contains("/some/path", "./contents")) diff --git a/source/blender/blenlib/tests/BLI_string_test.cc b/source/blender/blenlib/tests/BLI_string_test.cc index 6c16af5767c..eaaa65dd39f 100644 --- a/source/blender/blenlib/tests/BLI_string_test.cc +++ b/source/blender/blenlib/tests/BLI_string_test.cc @@ -420,98 +420,98 @@ TEST(string, StrFormatByteUnits) EXPECT_STREQ("-8191.8472 PiB", size_str); } -/* BLI_str_format_attribute_domain_size */ -TEST(string, StrFormatAttributeDomainSize) +/* BLI_str_format_decimal_unit */ +TEST(string, StrFormatDecimalUnits) { char size_str[7]; int size; - BLI_str_format_attribute_domain_size(size_str, size = 0); + BLI_str_format_decimal_unit(size_str, size = 0); EXPECT_STREQ("0", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1); + BLI_str_format_decimal_unit(size_str, size = 1); EXPECT_STREQ("1", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 10); + BLI_str_format_decimal_unit(size_str, size = 10); EXPECT_STREQ("10", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 15); + BLI_str_format_decimal_unit(size_str, size = 15); EXPECT_STREQ("15", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 100); + BLI_str_format_decimal_unit(size_str, size = 100); EXPECT_STREQ("100", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 155); + BLI_str_format_decimal_unit(size_str, size = 155); EXPECT_STREQ("155", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1000); + BLI_str_format_decimal_unit(size_str, size = 1000); EXPECT_STREQ("1.0K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1555); + BLI_str_format_decimal_unit(size_str, size = 1555); EXPECT_STREQ("1.6K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 10000); + BLI_str_format_decimal_unit(size_str, size = 10000); EXPECT_STREQ("10.0K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 15555); + BLI_str_format_decimal_unit(size_str, size = 15555); EXPECT_STREQ("15.6K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 100000); + BLI_str_format_decimal_unit(size_str, size = 100000); EXPECT_STREQ("100K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 100000); + BLI_str_format_decimal_unit(size_str, size = 100000); EXPECT_STREQ("100K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 155555); + BLI_str_format_decimal_unit(size_str, size = 155555); EXPECT_STREQ("156K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1000000); + BLI_str_format_decimal_unit(size_str, size = 1000000); EXPECT_STREQ("1.0M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1555555); + BLI_str_format_decimal_unit(size_str, size = 1555555); EXPECT_STREQ("1.6M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 10000000); + BLI_str_format_decimal_unit(size_str, size = 10000000); EXPECT_STREQ("10.0M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 15555555); + BLI_str_format_decimal_unit(size_str, size = 15555555); EXPECT_STREQ("15.6M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 100000000); + BLI_str_format_decimal_unit(size_str, size = 100000000); EXPECT_STREQ("100M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 155555555); + BLI_str_format_decimal_unit(size_str, size = 155555555); EXPECT_STREQ("156M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1000000000); + BLI_str_format_decimal_unit(size_str, size = 1000000000); EXPECT_STREQ("1.0B", size_str); /* Largest possible value. */ - BLI_str_format_attribute_domain_size(size_str, size = INT32_MAX); + BLI_str_format_decimal_unit(size_str, size = INT32_MAX); EXPECT_STREQ("2.1B", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -0); + BLI_str_format_decimal_unit(size_str, size = -0); EXPECT_STREQ("0", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1); + BLI_str_format_decimal_unit(size_str, size = -1); EXPECT_STREQ("-1", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -10); + BLI_str_format_decimal_unit(size_str, size = -10); EXPECT_STREQ("-10", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -15); + BLI_str_format_decimal_unit(size_str, size = -15); EXPECT_STREQ("-15", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -100); + BLI_str_format_decimal_unit(size_str, size = -100); EXPECT_STREQ("-100", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -155); + BLI_str_format_decimal_unit(size_str, size = -155); EXPECT_STREQ("-155", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1000); + BLI_str_format_decimal_unit(size_str, size = -1000); EXPECT_STREQ("-1.0K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1555); + BLI_str_format_decimal_unit(size_str, size = -1555); EXPECT_STREQ("-1.6K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -10000); + BLI_str_format_decimal_unit(size_str, size = -10000); EXPECT_STREQ("-10.0K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -15555); + BLI_str_format_decimal_unit(size_str, size = -15555); EXPECT_STREQ("-15.6K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -100000); + BLI_str_format_decimal_unit(size_str, size = -100000); EXPECT_STREQ("-100K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -155555); + BLI_str_format_decimal_unit(size_str, size = -155555); EXPECT_STREQ("-156K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1000000); + BLI_str_format_decimal_unit(size_str, size = -1000000); EXPECT_STREQ("-1.0M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1555555); + BLI_str_format_decimal_unit(size_str, size = -1555555); EXPECT_STREQ("-1.6M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -10000000); + BLI_str_format_decimal_unit(size_str, size = -10000000); EXPECT_STREQ("-10.0M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -15555555); + BLI_str_format_decimal_unit(size_str, size = -15555555); EXPECT_STREQ("-15.6M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -100000000); + BLI_str_format_decimal_unit(size_str, size = -100000000); EXPECT_STREQ("-100M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -155555555); + BLI_str_format_decimal_unit(size_str, size = -155555555); EXPECT_STREQ("-156M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1000000000); + BLI_str_format_decimal_unit(size_str, size = -1000000000); EXPECT_STREQ("-1.0B", size_str); /* Smallest possible value. */ - BLI_str_format_attribute_domain_size(size_str, size = -INT32_MAX); + BLI_str_format_decimal_unit(size_str, size = -INT32_MAX); EXPECT_STREQ("-2.1B", size_str); } diff --git a/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc b/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc index 0ff488202c2..09bb1e7239f 100644 --- a/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc +++ b/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc @@ -57,23 +57,23 @@ static void str_ghash_tests(GHash *ghash, const char *id) printf("\n========== STARTING %s ==========\n", id); #ifdef TEXT_CORPUS_PATH - size_t sz = 0; + size_t data_size = 0; char *data; { struct stat st; if (stat(TEXT_CORPUS_PATH, &st) == 0) - sz = st.st_size; + data_size = st.st_size; } - if (sz != 0) { + if (data_size != 0) { FILE *f = fopen(TEXT_CORPUS_PATH, "r"); - data = (char *)MEM_mallocN(sz + 1, __func__); - if (fread(data, sizeof(*data), sz, f) != sz) { + data = (char *)MEM_mallocN(data_size + 1, __func__); + if (fread(data, sizeof(*data), data_size, f) != data_size) { printf("ERROR in reading file %s!", TEXT_CORPUS_PATH); MEM_freeN(data); data = BLI_strdup(words10k); } - data[sz] = '\0'; + data[data_size] = '\0'; fclose(f); } else { diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index c1274de034d..043f9ffd723 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -156,10 +156,11 @@ BlendFileData *BLO_read_from_memory(const void *mem, * * \param oldmain: old main, * from which we will keep libraries and other data-blocks that should not have changed. - * \param filename: current file, only for retrieving library data. + * \param filepath: current file, only for retrieving library data. + * Typically `BKE_main_blendfile_path(oldmain)`. */ BlendFileData *BLO_read_from_memfile(struct Main *oldmain, - const char *filename, + const char *filepath, struct MemFile *memfile, const struct BlendFileReadParams *params, struct ReportList *reports); diff --git a/source/blender/blenloader/BLO_undofile.h b/source/blender/blenloader/BLO_undofile.h index 48334444c4c..0584f95d85f 100644 --- a/source/blender/blenloader/BLO_undofile.h +++ b/source/blender/blenloader/BLO_undofile.h @@ -46,7 +46,7 @@ typedef struct MemFileWriteData { } MemFileWriteData; typedef struct MemFileUndoData { - char filename[1024]; /* FILE_MAX */ + char filepath[1024]; /* FILE_MAX */ MemFile memfile; size_t undo_size; } MemFileUndoData; @@ -98,6 +98,6 @@ extern struct Main *BLO_memfile_main_get(struct MemFile *memfile, * * \return success. */ -extern bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename); +extern bool BLO_memfile_write_file(struct MemFile *memfile, const char *filepath); FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction); diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c index 10b69b67fa1..1bfa3b0e2bb 100644 --- a/source/blender/blenloader/intern/readblenentry.c +++ b/source/blender/blenloader/intern/readblenentry.c @@ -388,7 +388,7 @@ BlendFileData *BLO_read_from_memory(const void *mem, } BlendFileData *BLO_read_from_memfile(Main *oldmain, - const char *filename, + const char *filepath, MemFile *memfile, const struct BlendFileReadParams *params, ReportList *reports) @@ -401,7 +401,7 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain, fd = blo_filedata_from_memfile(memfile, params, &bf_reports); if (fd) { fd->skip_flags = params->skip_flags; - BLI_strncpy(fd->relabase, filename, sizeof(fd->relabase)); + BLI_strncpy(fd->relabase, filepath, sizeof(fd->relabase)); /* separate libraries from old main */ blo_split_main(&old_mainlist, oldmain); @@ -420,7 +420,7 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain, * read IDs whenever possible. */ blo_cache_storage_init(fd, oldmain); - bfd = blo_read_file_internal(fd, filename); + bfd = blo_read_file_internal(fd, filepath); /* Ensure relinked caches are not freed together with their old IDs. */ blo_cache_storage_old_bmain_clear(fd, oldmain); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 27890a908ab..973965ada50 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -477,7 +477,6 @@ static void split_libdata(ListBase *lb_src, Main **lib_main_array, const uint li } else { CLOG_ERROR(&LOG, "Invalid library for '%s'", id->name); - BLI_assert(0); } } } @@ -1477,14 +1476,14 @@ BlendThumbnail *BLO_thumbnail_from_file(const char *filepath) const int width = fd_data[0]; const int height = fd_data[1]; if (BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) { - const size_t sz = BLEN_THUMB_MEMSIZE(width, height); - data = MEM_mallocN(sz, __func__); + const size_t data_size = BLEN_THUMB_MEMSIZE(width, height); + data = MEM_mallocN(data_size, __func__); if (data) { - BLI_assert((sz - sizeof(*data)) == + BLI_assert((data_size - sizeof(*data)) == (BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*fd_data) * 2))); data->width = width; data->height = height; - memcpy(data->rect, &fd_data[2], sz - sizeof(*data)); + memcpy(data->rect, &fd_data[2], data_size - sizeof(*data)); } } } @@ -2068,7 +2067,7 @@ static void direct_link_id_embedded_id(BlendDataReader *reader, static int direct_link_id_restore_recalc_exceptions(const ID *id_current) { /* Exception for armature objects, where the pose has direct points to the - * armature databolock. */ + * armature data-block. */ if (GS(id_current->name) == ID_OB && ((Object *)id_current)->pose) { return ID_RECALC_GEOMETRY; } @@ -2413,7 +2412,7 @@ static int lib_link_main_data_restore_cb(LibraryIDLinkCallbackData *cb_data) if (collection->flag & COLLECTION_IS_MASTER) { /* We should never reach that point anymore, since master collection private ID should be * properly tagged with IDWALK_CB_EMBEDDED. */ - BLI_assert(0); + BLI_assert_unreachable(); return IDWALK_RET_NOP; } } @@ -3857,14 +3856,14 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) const int width = data[0]; const int height = data[1]; if (BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) { - const size_t sz = BLEN_THUMB_MEMSIZE(width, height); - bfd->main->blen_thumb = MEM_mallocN(sz, __func__); + const size_t data_size = BLEN_THUMB_MEMSIZE(width, height); + bfd->main->blen_thumb = MEM_mallocN(data_size, __func__); - BLI_assert((sz - sizeof(*bfd->main->blen_thumb)) == + BLI_assert((data_size - sizeof(*bfd->main->blen_thumb)) == (BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*data) * 2))); bfd->main->blen_thumb->width = width; bfd->main->blen_thumb->height = height; - memcpy(bfd->main->blen_thumb->rect, &data[2], sz - sizeof(*bfd->main->blen_thumb)); + memcpy(bfd->main->blen_thumb->rect, &data[2], data_size - sizeof(*bfd->main->blen_thumb)); } } } diff --git a/source/blender/blenloader/intern/undofile.c b/source/blender/blenloader/intern/undofile.c index 5c4598b85c5..b5c2a73c268 100644 --- a/source/blender/blenloader/intern/undofile.c +++ b/source/blender/blenloader/intern/undofile.c @@ -120,7 +120,7 @@ void BLO_memfile_write_init(MemFileWriteData *mem_data, *entry = mem_chunk; } else { - BLI_assert(0); + BLI_assert_unreachable(); } } } @@ -195,7 +195,7 @@ struct Main *BLO_memfile_main_get(struct MemFile *memfile, return bmain_undo; } -bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename) +bool BLO_memfile_write_file(struct MemFile *memfile, const char *filepath) { MemFileChunk *chunk; int file, oflags; @@ -216,12 +216,12 @@ bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename) # warning "Symbolic links will be followed on undo save, possibly causing CVE-2008-1103" # endif #endif - file = BLI_open(filename, oflags, 0666); + file = BLI_open(filepath, oflags, 0666); if (file == -1) { fprintf(stderr, "Unable to save '%s': %s\n", - filename, + filepath, errno ? strerror(errno) : "Unknown error opening file"); return false; } @@ -242,7 +242,7 @@ bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename) if (chunk) { fprintf(stderr, "Unable to save '%s': %s\n", - filename, + filepath, errno ? strerror(errno) : "Unknown error writing file"); return false; } diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index d992d426b9a..eeec55a7b06 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -4892,7 +4892,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) /* Default Face Set Color. */ for (Mesh *me = bmain->meshes.first; me != NULL; me = me->id.next) { if (me->totpoly > 0) { - int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); + const int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); if (face_sets) { me->face_sets_color_default = abs(face_sets[0]); } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index e6a214452fe..de47fe49946 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -2735,6 +2735,13 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } FOREACH_NODETREE_END; + + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type == NTREE_GEOMETRY) { + version_node_input_socket_name( + ntree, GEO_NODE_SUBDIVISION_SURFACE, "Crease", "Edge Crease"); + } + } } if (!MAIN_VERSION_ATLEAST(bmain, 302, 13)) { @@ -2772,5 +2779,271 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + /* Replace legacy combine/separate color nodes */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + /* In geometry nodes, replace shader combine/separate color nodes with function nodes */ + if (ntree->type == NTREE_GEOMETRY) { + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "R", "Red"); + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "G", "Green"); + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "B", "Blue"); + version_node_output_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "Image", "Color"); + + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "R", "Red"); + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "G", "Green"); + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "B", "Blue"); + version_node_input_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "Image", "Color"); + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + switch (node->type) { + case SH_NODE_COMBRGB_LEGACY: { + node->type = FN_NODE_COMBINE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "FunctionNodeCombineColor"); + node->storage = storage; + break; + } + case SH_NODE_SEPRGB_LEGACY: { + node->type = FN_NODE_SEPARATE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "FunctionNodeSeparateColor"); + node->storage = storage; + break; + } + } + } + } + + /* In compositing nodes, replace combine/separate RGBA/HSVA/YCbCrA/YCCA nodes with + * combine/separate color */ + if (ntree->type == NTREE_COMPOSIT) { + version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "R", "Red"); + version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "G", "Green"); + version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "B", "Blue"); + version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "A", "Alpha"); + + version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "H", "Red"); + version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "S", "Green"); + version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "V", "Blue"); + version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "A", "Alpha"); + + version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "Y", "Red"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "Cb", "Green"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "Cr", "Blue"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "A", "Alpha"); + + version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "Y", "Red"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "U", "Green"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "V", "Blue"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "A", "Alpha"); + + version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "R", "Red"); + version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "G", "Green"); + version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "B", "Blue"); + version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "A", "Alpha"); + + version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "H", "Red"); + version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "S", "Green"); + version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "V", "Blue"); + version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "A", "Alpha"); + + version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "Y", "Red"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "Cb", "Green"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "Cr", "Blue"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "A", "Alpha"); + + version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "Y", "Red"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "U", "Green"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "V", "Blue"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "A", "Alpha"); + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + switch (node->type) { + case CMP_NODE_COMBRGBA_LEGACY: { + node->type = CMP_NODE_COMBINE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "CompositorNodeCombineColor"); + node->storage = storage; + break; + } + case CMP_NODE_COMBHSVA_LEGACY: { + node->type = CMP_NODE_COMBINE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_HSV; + strcpy(node->idname, "CompositorNodeCombineColor"); + node->storage = storage; + break; + } + case CMP_NODE_COMBYCCA_LEGACY: { + node->type = CMP_NODE_COMBINE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_YCC; + storage->ycc_mode = node->custom1; + strcpy(node->idname, "CompositorNodeCombineColor"); + node->storage = storage; + break; + } + case CMP_NODE_COMBYUVA_LEGACY: { + node->type = CMP_NODE_COMBINE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_YUV; + strcpy(node->idname, "CompositorNodeCombineColor"); + node->storage = storage; + break; + } + case CMP_NODE_SEPRGBA_LEGACY: { + node->type = CMP_NODE_SEPARATE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "CompositorNodeSeparateColor"); + node->storage = storage; + break; + } + case CMP_NODE_SEPHSVA_LEGACY: { + node->type = CMP_NODE_SEPARATE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_HSV; + strcpy(node->idname, "CompositorNodeSeparateColor"); + node->storage = storage; + break; + } + case CMP_NODE_SEPYCCA_LEGACY: { + node->type = CMP_NODE_SEPARATE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_YCC; + storage->ycc_mode = node->custom1; + strcpy(node->idname, "CompositorNodeSeparateColor"); + node->storage = storage; + break; + } + case CMP_NODE_SEPYUVA_LEGACY: { + node->type = CMP_NODE_SEPARATE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_YUV; + strcpy(node->idname, "CompositorNodeSeparateColor"); + node->storage = storage; + break; + } + } + } + } + + /* In texture nodes, replace combine/separate RGBA with combine/separate color */ + if (ntree->type == NTREE_TEXTURE) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + switch (node->type) { + case TEX_NODE_COMPOSE_LEGACY: { + node->type = TEX_NODE_COMBINE_COLOR; + node->custom1 = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "TextureNodeCombineColor"); + break; + } + case TEX_NODE_DECOMPOSE_LEGACY: { + node->type = TEX_NODE_SEPARATE_COLOR; + node->custom1 = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "TextureNodeSeparateColor"); + break; + } + } + } + } + + /* In shader nodes, replace combine/separate RGB/HSV with combine/separate color */ + if (ntree->type == NTREE_SHADER) { + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "R", "Red"); + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "G", "Green"); + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "B", "Blue"); + version_node_output_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "Image", "Color"); + + version_node_input_socket_name(ntree, SH_NODE_COMBHSV_LEGACY, "H", "Red"); + version_node_input_socket_name(ntree, SH_NODE_COMBHSV_LEGACY, "S", "Green"); + version_node_input_socket_name(ntree, SH_NODE_COMBHSV_LEGACY, "V", "Blue"); + + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "R", "Red"); + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "G", "Green"); + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "B", "Blue"); + version_node_input_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "Image", "Color"); + + version_node_output_socket_name(ntree, SH_NODE_SEPHSV_LEGACY, "H", "Red"); + version_node_output_socket_name(ntree, SH_NODE_SEPHSV_LEGACY, "S", "Green"); + version_node_output_socket_name(ntree, SH_NODE_SEPHSV_LEGACY, "V", "Blue"); + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + switch (node->type) { + case SH_NODE_COMBRGB_LEGACY: { + node->type = SH_NODE_COMBINE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "ShaderNodeCombineColor"); + node->storage = storage; + break; + } + case SH_NODE_COMBHSV_LEGACY: { + node->type = SH_NODE_COMBINE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_HSV; + strcpy(node->idname, "ShaderNodeCombineColor"); + node->storage = storage; + break; + } + case SH_NODE_SEPRGB_LEGACY: { + node->type = SH_NODE_SEPARATE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "ShaderNodeSeparateColor"); + node->storage = storage; + break; + } + case SH_NODE_SEPHSV_LEGACY: { + node->type = SH_NODE_SEPARATE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_HSV; + strcpy(node->idname, "ShaderNodeSeparateColor"); + node->storage = storage; + break; + } + } + } + } + } + FOREACH_NODETREE_END; + + /* Initialize brush curves sculpt settings. */ + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + if (brush->ob_mode != OB_MODE_SCULPT_CURVES) { + continue; + } + if (brush->curves_sculpt_settings->points_per_curve == 0) { + brush->curves_sculpt_settings->points_per_curve = 8; + } + } + + /* UDIM Packing. */ + if (!DNA_struct_elem_find(fd->filesdna, "ImagePackedFile", "int", "tile_number")) { + for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { + int view; + LISTBASE_FOREACH_INDEX (ImagePackedFile *, imapf, &ima->packedfiles, view) { + imapf->view = view; + imapf->tile_number = 1001; + } + } + } } } diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 22db54d7609..65c42545a77 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -73,6 +73,8 @@ #include "BLI_utildefines.h" +#include "CLG_log.h" + /* allow writefile to use deprecated functionality (for forward compatibility code) */ #define DNA_DEPRECATED_ALLOW @@ -133,6 +135,8 @@ #define ZSTD_COMPRESSION_LEVEL 3 +static CLG_LogRef LOG = {"blo.writefile"}; + /** Use if we want to store how many bytes have been written to the file. */ // #define USE_WRITE_DATA_LEN @@ -973,7 +977,7 @@ static void write_libraries(WriteData *wd, Main *main) if (main->curlib->packedfile) { BKE_packedfile_blend_write(&writer, main->curlib->packedfile); if (wd->use_memfile == false) { - printf("write packed .blend: %s\n", main->curlib->filepath); + CLOG_INFO(&LOG, 2, "Write packed .blend: %s\n", main->curlib->filepath); } } @@ -984,12 +988,11 @@ static void write_libraries(WriteData *wd, Main *main) ((id->tag & LIB_TAG_EXTERN) || ((id->tag & LIB_TAG_INDIRECT) && (id->flag & LIB_INDIRECT_WEAK_LINK)))) { if (!BKE_idtype_idcode_is_linkable(GS(id->name))) { - printf( - "ERROR: write file: data-block '%s' from lib '%s' is not linkable " - "but is flagged as directly linked\n", - id->name, - main->curlib->filepath_abs); - BLI_assert(0); + CLOG_ERROR(&LOG, + "Data-block '%s' from lib '%s' is not linkable, but is flagged as " + "directly linked\n", + id->name, + main->curlib->filepath_abs); } writestruct(wd, ID_LINK_PLACEHOLDER, ID, 1, id); } @@ -1129,9 +1132,15 @@ static bool write_file_handle(Main *mainvar, char id_buffer_static[ID_BUFFER_STATIC_SIZE]; void *id_buffer = id_buffer_static; - const size_t idtype_struct_size = BKE_idtype_get_info_from_id(id)->struct_size; + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + const size_t idtype_struct_size = id_type->struct_size; if (idtype_struct_size > ID_BUFFER_STATIC_SIZE) { - BLI_assert(0); + CLOG_ERROR(&LOG, + "ID maximum buffer size (%d bytes) is not big enough to fit IDs of type %s, " + "which needs %lu bytes", + ID_BUFFER_STATIC_SIZE, + id_type->name, + id_type->struct_size); id_buffer = MEM_mallocN(idtype_struct_size, __func__); } @@ -1200,7 +1209,6 @@ static bool write_file_handle(Main *mainvar, * #direct_link_id_common in `readfile.c` anyway, */ ((ID *)id_buffer)->py_instance = 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); } @@ -1415,7 +1423,7 @@ bool BLO_write_file(Main *mainvar, BKE_bpath_absolute_convert(mainvar, dir_src, NULL); break; case BLO_WRITE_PATH_REMAP_NONE: - BLI_assert(0); /* Unreachable. */ + BLI_assert_unreachable(); /* Unreachable. */ break; } @@ -1490,7 +1498,7 @@ void BLO_write_struct_array_by_name(BlendWriter *writer, { int struct_id = BLO_get_struct_id_by_name(writer, struct_name); if (UNLIKELY(struct_id == -1)) { - printf("error: can't find SDNA code <%s>\n", struct_name); + CLOG_ERROR(&LOG, "Can't find SDNA code <%s>\n", struct_name); return; } BLO_write_struct_array_by_id(writer, struct_id, array_size, data_ptr); @@ -1538,7 +1546,7 @@ void BLO_write_struct_list_by_name(BlendWriter *writer, const char *struct_name, { int struct_id = BLO_get_struct_id_by_name(writer, struct_name); if (UNLIKELY(struct_id == -1)) { - printf("error: can't find SDNA code <%s>\n", struct_name); + CLOG_ERROR(&LOG, "Can't find SDNA code <%s>\n", struct_name); return; } BLO_write_struct_list_by_id(writer, struct_id, list); diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index 0246850123a..9d5737a5b71 100644 --- a/source/blender/bmesh/bmesh_class.h +++ b/source/blender/bmesh/bmesh_class.h @@ -265,8 +265,20 @@ typedef struct BMFace { * (the length of #BMFace.l_first circular linked list). */ int len; - float no[3]; /* face normal */ - short mat_nr; /* material index */ + /** + * Face normal, see #BM_face_calc_normal. + */ + float no[3]; + /** + * Material index, typically >= 0 and < #Mesh.totcol although this isn't enforced + * Python for e.g. can set this to any positive value since scripts may create + * mesh data first and setup material slots later. + * + * When using to index into a material array it's range should be checked first, + * values exceeding the range should be ignored or treated as zero + * (if a material slot needs to be used - when drawing for e.g.) + */ + short mat_nr; // short _pad[3]; } BMFace; diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index d6bf556f14b..4d84d558cd7 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -1460,7 +1460,7 @@ BMFace *bmesh_kernel_split_face_make_edge(BMesh *bm, } else { /* this code is not significant until holes actually work */ - // printf("warning: call to split face euler without holes argument; holes will be tossed.\n"); + // printf("WARNING: call to split face euler without holes argument; holes will be tossed.\n"); for (lst = f->loops.last; lst != f->loops.first; lst = lst2) { lst2 = lst->prev; BLI_mempool_free(bm->looplistpool, lst); diff --git a/source/blender/bmesh/intern/bmesh_mods.c b/source/blender/bmesh/intern/bmesh_mods.c index 70710edea5c..8b56e08c22b 100644 --- a/source/blender/bmesh/intern/bmesh_mods.c +++ b/source/blender/bmesh/intern/bmesh_mods.c @@ -597,7 +597,7 @@ bool BM_face_validate(BMFace *face, FILE *err) bool ret = true; if (face->len == 2) { - fprintf(err, "warning: found two-edged face. face ptr: %p\n", face); + fprintf(err, "WARNING: found two-edged face. face ptr: %p\n", face); fflush(err); } diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index 9ea16c8b61c..6df446a377c 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -415,7 +415,7 @@ void BM_face_calc_tangent_edge_diagonal(const BMFace *f, float r_tangent[3]) /* In case of degenerate faces. */ zero_v3(r_tangent); - /* warning: O(n^2) loop here, take care! */ + /* WARNING: O(n^2) loop here, take care! */ float dist_max_sq = 0.0f; do { BMLoop *l_iter_other = l_iter->next; @@ -447,7 +447,7 @@ void BM_face_calc_tangent_vert_diagonal(const BMFace *f, float r_tangent[3]) /* In case of degenerate faces. */ zero_v3(r_tangent); - /* warning: O(n^2) loop here, take care! */ + /* WARNING: O(n^2) loop here, take care! */ float dist_max_sq = 0.0f; do { BMLoop *l_iter_other = l_iter->next; diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 8f32f878c58..fa852cdd6da 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -5354,7 +5354,7 @@ static BMEdge *snap_edge_for_center_vmesh_vert(int i, * so the arguments bndv_rep_faces is an array of size n_bndv give the freps for each i, * and center_frep is the frep for the center. * - * Note: this function is for edge bevels only, at the moment. + * NOTE: this function is for edge bevels only, at the moment. */ static void snap_edges_for_vmesh_vert(int i, int j, diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 66f813e02b2..55e349423bb 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -259,12 +259,16 @@ set(SRC # converter nodes nodes/COM_CombineColorNode.cc nodes/COM_CombineColorNode.h + nodes/COM_CombineColorNodeLegacy.cc + nodes/COM_CombineColorNodeLegacy.h nodes/COM_CombineXYZNode.cc nodes/COM_CombineXYZNode.h nodes/COM_IDMaskNode.cc nodes/COM_IDMaskNode.h nodes/COM_SeparateColorNode.cc nodes/COM_SeparateColorNode.h + nodes/COM_SeparateColorNodeLegacy.cc + nodes/COM_SeparateColorNodeLegacy.h nodes/COM_SeparateXYZNode.cc nodes/COM_SeparateXYZNode.h diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc index 13d412a338b..6d7341376e9 100644 --- a/source/blender/compositor/intern/COM_Converter.cc +++ b/source/blender/compositor/intern/COM_Converter.cc @@ -29,6 +29,7 @@ #include "COM_ColorSpillNode.h" #include "COM_ColorToBWNode.h" #include "COM_CombineColorNode.h" +#include "COM_CombineColorNodeLegacy.h" #include "COM_CombineXYZNode.h" #include "COM_CompositorNode.h" #include "COM_ConvertAlphaNode.h" @@ -82,6 +83,7 @@ #include "COM_ScaleOperation.h" #include "COM_SceneTimeNode.h" #include "COM_SeparateColorNode.h" +#include "COM_SeparateColorNodeLegacy.h" #include "COM_SeparateXYZNode.h" #include "COM_SetAlphaNode.h" #include "COM_SetValueOperation.h" @@ -169,28 +171,34 @@ Node *COM_convert_bnode(bNode *b_node) case CMP_NODE_BRIGHTCONTRAST: node = new BrightnessNode(b_node); break; - case CMP_NODE_SEPRGBA: + case CMP_NODE_SEPARATE_COLOR: + node = new SeparateColorNode(b_node); + break; + case CMP_NODE_COMBINE_COLOR: + node = new CombineColorNode(b_node); + break; + case CMP_NODE_SEPRGBA_LEGACY: node = new SeparateRGBANode(b_node); break; - case CMP_NODE_COMBRGBA: + case CMP_NODE_COMBRGBA_LEGACY: node = new CombineRGBANode(b_node); break; - case CMP_NODE_SEPHSVA: + case CMP_NODE_SEPHSVA_LEGACY: node = new SeparateHSVANode(b_node); break; - case CMP_NODE_COMBHSVA: + case CMP_NODE_COMBHSVA_LEGACY: node = new CombineHSVANode(b_node); break; - case CMP_NODE_SEPYUVA: + case CMP_NODE_SEPYUVA_LEGACY: node = new SeparateYUVANode(b_node); break; - case CMP_NODE_COMBYUVA: + case CMP_NODE_COMBYUVA_LEGACY: node = new CombineYUVANode(b_node); break; - case CMP_NODE_SEPYCCA: + case CMP_NODE_SEPYCCA_LEGACY: node = new SeparateYCCANode(b_node); break; - case CMP_NODE_COMBYCCA: + case CMP_NODE_COMBYCCA_LEGACY: node = new CombineYCCANode(b_node); break; case CMP_NODE_ALPHAOVER: diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index 66dbabe71b5..d0f0be590f6 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -420,7 +420,7 @@ void DebugInfo::graphviz(const ExecutionSystem *system, StringRefNull name) char *str = (char *)MEM_mallocN(max_textlength, __func__); if (graphviz_system(system, str, max_textlength - 1)) { char basename[FILE_MAX]; - char filename[FILE_MAX]; + char filepath[FILE_MAX]; if (name.is_empty()) { BLI_snprintf(basename, sizeof(basename), "compositor_%d.dot", file_index_); @@ -428,12 +428,12 @@ void DebugInfo::graphviz(const ExecutionSystem *system, StringRefNull name) else { BLI_strncpy(basename, (name + ".dot").c_str(), sizeof(basename)); } - BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_session(), basename); + BLI_join_dirfile(filepath, sizeof(filepath), BKE_tempdir_session(), basename); file_index_++; - std::cout << "Writing compositor debug to: " << filename << "\n"; + std::cout << "Writing compositor debug to: " << filepath << "\n"; - FILE *fp = BLI_fopen(filename, "wb"); + FILE *fp = BLI_fopen(filepath, "wb"); fputs(str, fp); fclose(fp); } diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.cc b/source/blender/compositor/nodes/COM_CombineColorNode.cc index 0c8748fd2d6..ca2c59478fd 100644 --- a/source/blender/compositor/nodes/COM_CombineColorNode.cc +++ b/source/blender/compositor/nodes/COM_CombineColorNode.cc @@ -12,7 +12,7 @@ CombineColorNode::CombineColorNode(bNode *editor_node) : Node(editor_node) } void CombineColorNode::convert_to_operations(NodeConverter &converter, - const CompositorContext &context) const + const CompositorContext &UNUSED(context)) const { NodeInput *input_rsocket = this->get_input_socket(0); NodeInput *input_gsocket = this->get_input_socket(1); @@ -40,7 +40,39 @@ void CombineColorNode::convert_to_operations(NodeConverter &converter, converter.map_input_socket(input_bsocket, operation->get_input_socket(2)); converter.map_input_socket(input_asocket, operation->get_input_socket(3)); - NodeOperation *color_conv = get_color_converter(context); + bNode *editor_node = this->get_bnode(); + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)editor_node->storage; + + NodeOperation *color_conv = nullptr; + switch (storage->mode) { + case CMP_NODE_COMBSEP_COLOR_RGB: { + /* Pass */ + break; + } + case CMP_NODE_COMBSEP_COLOR_HSV: { + color_conv = new ConvertHSVToRGBOperation(); + break; + } + case CMP_NODE_COMBSEP_COLOR_HSL: { + color_conv = new ConvertHSLToRGBOperation(); + break; + } + case CMP_NODE_COMBSEP_COLOR_YCC: { + ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation(); + operation->set_mode(storage->ycc_mode); + color_conv = operation; + break; + } + case CMP_NODE_COMBSEP_COLOR_YUV: { + color_conv = new ConvertYUVToRGBOperation(); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } + if (color_conv) { converter.add_operation(color_conv); @@ -52,27 +84,4 @@ void CombineColorNode::convert_to_operations(NodeConverter &converter, } } -NodeOperation *CombineRGBANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return nullptr; /* no conversion needed */ -} - -NodeOperation *CombineHSVANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return new ConvertHSVToRGBOperation(); -} - -NodeOperation *CombineYCCANode::get_color_converter(const CompositorContext & /*context*/) const -{ - ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation(); - bNode *editor_node = this->get_bnode(); - operation->set_mode(editor_node->custom1); - return operation; -} - -NodeOperation *CombineYUVANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return new ConvertYUVToRGBOperation(); -} - } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.h b/source/blender/compositor/nodes/COM_CombineColorNode.h index 2bead24cb02..7576cc9eb2d 100644 --- a/source/blender/compositor/nodes/COM_CombineColorNode.h +++ b/source/blender/compositor/nodes/COM_CombineColorNode.h @@ -12,45 +12,6 @@ class CombineColorNode : public Node { CombineColorNode(bNode *editor_node); void convert_to_operations(NodeConverter &converter, const CompositorContext &context) const override; - - protected: - virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0; -}; - -class CombineRGBANode : public CombineColorNode { - public: - CombineRGBANode(bNode *editor_node) : CombineColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class CombineHSVANode : public CombineColorNode { - public: - CombineHSVANode(bNode *editor_node) : CombineColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class CombineYCCANode : public CombineColorNode { - public: - CombineYCCANode(bNode *editor_node) : CombineColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class CombineYUVANode : public CombineColorNode { - public: - CombineYUVANode(bNode *editor_node) : CombineColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc new file mode 100644 index 00000000000..d5ba379bfc2 --- /dev/null +++ b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2011 Blender Foundation. */ + +#include "COM_CombineColorNodeLegacy.h" + +#include "COM_ConvertOperation.h" + +namespace blender::compositor { + +CombineColorNodeLegacy::CombineColorNodeLegacy(bNode *editor_node) : Node(editor_node) +{ +} + +void CombineColorNodeLegacy::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const +{ + NodeInput *input_rsocket = this->get_input_socket(0); + NodeInput *input_gsocket = this->get_input_socket(1); + NodeInput *input_bsocket = this->get_input_socket(2); + NodeInput *input_asocket = this->get_input_socket(3); + NodeOutput *output_socket = this->get_output_socket(0); + + CombineChannelsOperation *operation = new CombineChannelsOperation(); + if (input_rsocket->is_linked()) { + operation->set_canvas_input_index(0); + } + else if (input_gsocket->is_linked()) { + operation->set_canvas_input_index(1); + } + else if (input_bsocket->is_linked()) { + operation->set_canvas_input_index(2); + } + else { + operation->set_canvas_input_index(3); + } + converter.add_operation(operation); + + converter.map_input_socket(input_rsocket, operation->get_input_socket(0)); + converter.map_input_socket(input_gsocket, operation->get_input_socket(1)); + converter.map_input_socket(input_bsocket, operation->get_input_socket(2)); + converter.map_input_socket(input_asocket, operation->get_input_socket(3)); + + NodeOperation *color_conv = get_color_converter(context); + if (color_conv) { + converter.add_operation(color_conv); + + converter.add_link(operation->get_output_socket(), color_conv->get_input_socket(0)); + converter.map_output_socket(output_socket, color_conv->get_output_socket()); + } + else { + converter.map_output_socket(output_socket, operation->get_output_socket()); + } +} + +NodeOperation *CombineRGBANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return nullptr; /* no conversion needed */ +} + +NodeOperation *CombineHSVANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return new ConvertHSVToRGBOperation(); +} + +NodeOperation *CombineYCCANode::get_color_converter(const CompositorContext & /*context*/) const +{ + ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation(); + bNode *editor_node = this->get_bnode(); + operation->set_mode(editor_node->custom1); + return operation; +} + +NodeOperation *CombineYUVANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return new ConvertYUVToRGBOperation(); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.h b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.h new file mode 100644 index 00000000000..edc66c8932f --- /dev/null +++ b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2011 Blender Foundation. */ + +#pragma once + +#include "COM_Node.h" + +namespace blender::compositor { + +class CombineColorNodeLegacy : public Node { + public: + CombineColorNodeLegacy(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; + + protected: + virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0; +}; + +class CombineRGBANode : public CombineColorNodeLegacy { + public: + CombineRGBANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class CombineHSVANode : public CombineColorNodeLegacy { + public: + CombineHSVANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class CombineYCCANode : public CombineColorNodeLegacy { + public: + CombineYCCANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class CombineYUVANode : public CombineColorNodeLegacy { + public: + CombineYUVANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.cc b/source/blender/compositor/nodes/COM_SeparateColorNode.cc index 221d80e67f2..a956c02ed42 100644 --- a/source/blender/compositor/nodes/COM_SeparateColorNode.cc +++ b/source/blender/compositor/nodes/COM_SeparateColorNode.cc @@ -12,7 +12,7 @@ SeparateColorNode::SeparateColorNode(bNode *editor_node) : Node(editor_node) } void SeparateColorNode::convert_to_operations(NodeConverter &converter, - const CompositorContext &context) const + const CompositorContext &UNUSED(context)) const { NodeInput *image_socket = this->get_input_socket(0); NodeOutput *output_rsocket = this->get_output_socket(0); @@ -20,7 +20,39 @@ void SeparateColorNode::convert_to_operations(NodeConverter &converter, NodeOutput *output_bsocket = this->get_output_socket(2); NodeOutput *output_asocket = this->get_output_socket(3); - NodeOperation *color_conv = get_color_converter(context); + bNode *editor_node = this->get_bnode(); + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)editor_node->storage; + + NodeOperation *color_conv = nullptr; + switch (storage->mode) { + case CMP_NODE_COMBSEP_COLOR_RGB: { + /* Pass */ + break; + } + case CMP_NODE_COMBSEP_COLOR_HSV: { + color_conv = new ConvertRGBToHSVOperation(); + break; + } + case CMP_NODE_COMBSEP_COLOR_HSL: { + color_conv = new ConvertRGBToHSLOperation(); + break; + } + case CMP_NODE_COMBSEP_COLOR_YCC: { + ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation(); + operation->set_mode(storage->ycc_mode); + color_conv = operation; + break; + } + case CMP_NODE_COMBSEP_COLOR_YUV: { + color_conv = new ConvertRGBToYUVOperation(); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } + if (color_conv) { converter.add_operation(color_conv); @@ -84,27 +116,4 @@ void SeparateColorNode::convert_to_operations(NodeConverter &converter, } } -NodeOperation *SeparateRGBANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return nullptr; /* no conversion needed */ -} - -NodeOperation *SeparateHSVANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return new ConvertRGBToHSVOperation(); -} - -NodeOperation *SeparateYCCANode::get_color_converter(const CompositorContext & /*context*/) const -{ - ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation(); - bNode *editor_node = this->get_bnode(); - operation->set_mode(editor_node->custom1); - return operation; -} - -NodeOperation *SeparateYUVANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return new ConvertRGBToYUVOperation(); -} - } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.h b/source/blender/compositor/nodes/COM_SeparateColorNode.h index 78ab0959f43..6adb2d0bb22 100644 --- a/source/blender/compositor/nodes/COM_SeparateColorNode.h +++ b/source/blender/compositor/nodes/COM_SeparateColorNode.h @@ -12,45 +12,6 @@ class SeparateColorNode : public Node { SeparateColorNode(bNode *editor_node); void convert_to_operations(NodeConverter &converter, const CompositorContext &context) const override; - - protected: - virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0; -}; - -class SeparateRGBANode : public SeparateColorNode { - public: - SeparateRGBANode(bNode *editor_node) : SeparateColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class SeparateHSVANode : public SeparateColorNode { - public: - SeparateHSVANode(bNode *editor_node) : SeparateColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class SeparateYCCANode : public SeparateColorNode { - public: - SeparateYCCANode(bNode *editor_node) : SeparateColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class SeparateYUVANode : public SeparateColorNode { - public: - SeparateYUVANode(bNode *editor_node) : SeparateColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc new file mode 100644 index 00000000000..c3728bc152f --- /dev/null +++ b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2011 Blender Foundation. */ + +#include "COM_SeparateColorNodeLegacy.h" + +#include "COM_ConvertOperation.h" + +namespace blender::compositor { + +SeparateColorNodeLegacy::SeparateColorNodeLegacy(bNode *editor_node) : Node(editor_node) +{ +} + +void SeparateColorNodeLegacy::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const +{ + NodeInput *image_socket = this->get_input_socket(0); + NodeOutput *output_rsocket = this->get_output_socket(0); + NodeOutput *output_gsocket = this->get_output_socket(1); + NodeOutput *output_bsocket = this->get_output_socket(2); + NodeOutput *output_asocket = this->get_output_socket(3); + + NodeOperation *color_conv = get_color_converter(context); + if (color_conv) { + converter.add_operation(color_conv); + + converter.map_input_socket(image_socket, color_conv->get_input_socket(0)); + } + + { + SeparateChannelOperation *operation = new SeparateChannelOperation(); + operation->set_channel(0); + converter.add_operation(operation); + + if (color_conv) { + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); + } + else { + converter.map_input_socket(image_socket, operation->get_input_socket(0)); + } + converter.map_output_socket(output_rsocket, operation->get_output_socket(0)); + } + + { + SeparateChannelOperation *operation = new SeparateChannelOperation(); + operation->set_channel(1); + converter.add_operation(operation); + + if (color_conv) { + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); + } + else { + converter.map_input_socket(image_socket, operation->get_input_socket(0)); + } + converter.map_output_socket(output_gsocket, operation->get_output_socket(0)); + } + + { + SeparateChannelOperation *operation = new SeparateChannelOperation(); + operation->set_channel(2); + converter.add_operation(operation); + + if (color_conv) { + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); + } + else { + converter.map_input_socket(image_socket, operation->get_input_socket(0)); + } + converter.map_output_socket(output_bsocket, operation->get_output_socket(0)); + } + + { + SeparateChannelOperation *operation = new SeparateChannelOperation(); + operation->set_channel(3); + converter.add_operation(operation); + + if (color_conv) { + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); + } + else { + converter.map_input_socket(image_socket, operation->get_input_socket(0)); + } + converter.map_output_socket(output_asocket, operation->get_output_socket(0)); + } +} + +NodeOperation *SeparateRGBANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return nullptr; /* no conversion needed */ +} + +NodeOperation *SeparateHSVANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return new ConvertRGBToHSVOperation(); +} + +NodeOperation *SeparateYCCANode::get_color_converter(const CompositorContext & /*context*/) const +{ + ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation(); + bNode *editor_node = this->get_bnode(); + operation->set_mode(editor_node->custom1); + return operation; +} + +NodeOperation *SeparateYUVANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return new ConvertRGBToYUVOperation(); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.h b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.h new file mode 100644 index 00000000000..10b33039c86 --- /dev/null +++ b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2011 Blender Foundation. */ + +#pragma once + +#include "COM_Node.h" + +namespace blender::compositor { + +class SeparateColorNodeLegacy : public Node { + public: + SeparateColorNodeLegacy(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; + + protected: + virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0; +}; + +class SeparateRGBANode : public SeparateColorNodeLegacy { + public: + SeparateRGBANode(bNode *editor_node) : SeparateColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class SeparateHSVANode : public SeparateColorNodeLegacy { + public: + SeparateHSVANode(bNode *editor_node) : SeparateColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class SeparateYCCANode : public SeparateColorNodeLegacy { + public: + SeparateYCCANode(bNode *editor_node) : SeparateColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class SeparateYUVANode : public SeparateColorNodeLegacy { + public: + SeparateYUVANode(bNode *editor_node) : SeparateColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc index 7579abf792a..24c0c577ac7 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertOperation.cc @@ -464,6 +464,68 @@ void ConvertHSVToRGBOperation::update_memory_buffer_partial(BuffersIterator<floa } } +/* ******** RGB to HSL ******** */ + +ConvertRGBToHSLOperation::ConvertRGBToHSLOperation() : ConvertBaseOperation() +{ + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); +} + +void ConvertRGBToHSLOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + rgb_to_hsl_v(input_color, output); + output[3] = input_color[3]; +} + +void ConvertRGBToHSLOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float *in = it.in(0); + rgb_to_hsl_v(in, it.out); + it.out[3] = in[3]; + } +} + +/* ******** HSL to RGB ******** */ + +ConvertHSLToRGBOperation::ConvertHSLToRGBOperation() : ConvertBaseOperation() +{ + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); +} + +void ConvertHSLToRGBOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + hsl_to_rgb_v(input_color, output); + output[0] = max_ff(output[0], 0.0f); + output[1] = max_ff(output[1], 0.0f); + output[2] = max_ff(output[2], 0.0f); + output[3] = input_color[3]; +} + +void ConvertHSLToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float *in = it.in(0); + hsl_to_rgb_v(in, it.out); + it.out[0] = max_ff(it.out[0], 0.0f); + it.out[1] = max_ff(it.out[1], 0.0f); + it.out[2] = max_ff(it.out[2], 0.0f); + it.out[3] = in[3]; + } +} + /* ******** Premul to Straight ******** */ ConvertPremulToStraightOperation::ConvertPremulToStraightOperation() : ConvertBaseOperation() diff --git a/source/blender/compositor/operations/COM_ConvertOperation.h b/source/blender/compositor/operations/COM_ConvertOperation.h index e1904d61d46..16d1e2e6bb5 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.h +++ b/source/blender/compositor/operations/COM_ConvertOperation.h @@ -172,6 +172,26 @@ class ConvertHSVToRGBOperation : public ConvertBaseOperation { void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; +class ConvertRGBToHSLOperation : public ConvertBaseOperation { + public: + ConvertRGBToHSLOperation(); + + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; +}; + +class ConvertHSLToRGBOperation : public ConvertBaseOperation { + public: + ConvertHSLToRGBOperation(); + + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; +}; + class ConvertPremulToStraightOperation : public ConvertBaseOperation { public: ConvertPremulToStraightOperation(); diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc index 573a740dac8..725751d15af 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc @@ -112,7 +112,7 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, double *X, *Y, *W; const unsigned int src_width = src->get_width(); const unsigned int src_height = src->get_height(); - unsigned int x, y, sz; + unsigned int x, y, src_dim_max; unsigned int i; float *buffer = src->get_buffer(); const uint8_t num_channels = src->get_num_channels(); @@ -202,10 +202,10 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, (void)0 /* Intermediate buffers. */ - sz = MAX2(src_width, src_height); - X = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss X buf"); - Y = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss Y buf"); - W = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss W buf"); + src_dim_max = MAX2(src_width, src_height); + X = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss X buf"); + Y = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss Y buf"); + W = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss W buf"); if (xy & 1) { /* H. */ int offset; for (y = 0; y < src_height; y++) { diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc index aeaf6b659e3..341541b4cdd 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc @@ -325,7 +325,6 @@ void OutputStereoOperation::deinit_execution() /* do colormanagement in the individual views, so it doesn't need to do in the stereo */ IMB_colormanagement_imbuf_for_write(ibuf[i], true, false, &format_); - IMB_prepare_write_ImBuf(IMB_isfloat(ibuf[i]), ibuf[i]); } /* create stereo buffer */ diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc index 372e0736cd2..49de275c256 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc @@ -219,6 +219,12 @@ OutputSingleLayerOperation::OutputSingleLayerOperation(const Scene *scene, image_input_ = nullptr; BKE_image_format_init_for_write(&format_, scene, format); + if (!save_as_render) { + /* If not saving as render, stop IMB_colormanagement_imbuf_for_write using this + * colorspace for conversion. */ + format_.linear_colorspace_settings.name[0] = '\0'; + } + BLI_strncpy(path_, path, sizeof(path_)); view_name_ = view_name; diff --git a/source/blender/depsgraph/DEG_depsgraph_query.h b/source/blender/depsgraph/DEG_depsgraph_query.h index ade75fa2f6f..12663e74d24 100644 --- a/source/blender/depsgraph/DEG_depsgraph_query.h +++ b/source/blender/depsgraph/DEG_depsgraph_query.h @@ -64,7 +64,7 @@ bool DEG_id_type_any_updated(const struct Depsgraph *depsgraph); bool DEG_id_type_any_exists(const struct Depsgraph *depsgraph, short id_type); /** Get additional evaluation flags for the given ID. */ -uint32_t DEG_get_eval_flags_for_id(const struct Depsgraph *graph, struct ID *id); +uint32_t DEG_get_eval_flags_for_id(const struct Depsgraph *graph, const struct ID *id); /** Get additional mesh CustomData_MeshMasks flags for the given object. */ void DEG_get_customdata_mask_for_object(const struct Depsgraph *graph, diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index fd569599b8b..6ffc711a475 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -32,6 +32,49 @@ #include "intern/eval/deg_eval_copy_on_write.h" #include "intern/node/deg_node_id.h" +namespace blender::deg { + +static const ID *get_original_id(const ID *id) +{ + if (id == nullptr) { + return nullptr; + } + if (id->orig_id == nullptr) { + return id; + } + BLI_assert((id->tag & LIB_TAG_COPIED_ON_WRITE) != 0); + return (ID *)id->orig_id; +} + +static ID *get_original_id(ID *id) +{ + const ID *const_id = id; + return const_cast<ID *>(get_original_id(const_id)); +} + +static const ID *get_evaluated_id(const Depsgraph *deg_graph, const ID *id) +{ + if (id == nullptr) { + return nullptr; + } + /* TODO(sergey): This is a duplicate of Depsgraph::get_cow_id(), + * but here we never do assert, since we don't know nature of the + * incoming ID data-block. */ + const IDNode *id_node = deg_graph->find_id_node(id); + if (id_node == nullptr) { + return id; + } + return id_node->id_cow; +} + +static ID *get_evaluated_id(const Depsgraph *deg_graph, ID *id) +{ + const ID *const_id = id; + return const_cast<ID *>(get_evaluated_id(deg_graph, const_id)); +} + +} // namespace blender::deg + namespace deg = blender::deg; struct Scene *DEG_get_input_scene(const Depsgraph *graph) @@ -90,7 +133,7 @@ bool DEG_id_type_any_exists(const Depsgraph *depsgraph, short id_type) return deg_graph->id_type_exist[BKE_idtype_idcode_to_index(id_type)] != 0; } -uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, ID *id) +uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, const ID *id) { if (graph == nullptr) { /* Happens when converting objects to mesh from a python script @@ -102,7 +145,7 @@ uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, ID *id) } const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph); - const deg::IDNode *id_node = deg_graph->find_id_node(DEG_get_original_id(id)); + const deg::IDNode *id_node = deg_graph->find_id_node(deg::get_original_id(id)); if (id_node == nullptr) { /* TODO(sergey): Does it mean we need to check set scene? */ return 0; @@ -171,18 +214,7 @@ Object *DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object) ID *DEG_get_evaluated_id(const Depsgraph *depsgraph, ID *id) { - if (id == nullptr) { - return nullptr; - } - /* TODO(sergey): This is a duplicate of Depsgraph::get_cow_id(), - * but here we never do assert, since we don't know nature of the - * incoming ID data-block. */ - const deg::Depsgraph *deg_graph = (const deg::Depsgraph *)depsgraph; - const deg::IDNode *id_node = deg_graph->find_id_node(id); - if (id_node == nullptr) { - return id; - } - return id_node->id_cow; + return deg::get_evaluated_id(reinterpret_cast<const deg::Depsgraph *>(depsgraph), id); } void DEG_get_evaluated_rna_pointer(const Depsgraph *depsgraph, @@ -249,14 +281,7 @@ Object *DEG_get_original_object(Object *object) ID *DEG_get_original_id(ID *id) { - if (id == nullptr) { - return nullptr; - } - if (id->orig_id == nullptr) { - return id; - } - BLI_assert((id->tag & LIB_TAG_COPIED_ON_WRITE) != 0); - return (ID *)id->orig_id; + return deg::get_original_id(id); } bool DEG_is_original_id(const ID *id) diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 1ff7585165b..e5aca26c43c 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -85,7 +85,7 @@ set(SRC intern/draw_curves.cc intern/draw_debug.c intern/draw_fluid.c - intern/draw_hair.c + intern/draw_hair.cc intern/draw_instance_data.c intern/draw_manager.c intern/draw_manager_data.c @@ -95,7 +95,7 @@ set(SRC intern/draw_manager_text.c intern/draw_manager_texture.c intern/draw_select_buffer.c - intern/draw_shader.c + intern/draw_shader.cc intern/draw_texture_pool.cc intern/draw_view.c intern/draw_view_data.cc @@ -133,6 +133,7 @@ set(SRC engines/eevee/eevee_subsurface.c engines/eevee/eevee_temporal_sampling.c engines/eevee/eevee_volumes.c + engines/eevee_next/eevee_camera.cc engines/eevee_next/eevee_engine.cc engines/eevee_next/eevee_instance.cc engines/eevee_next/eevee_material.cc @@ -352,6 +353,7 @@ set(GLSL_SRC engines/eevee/shaders/world_vert.glsl engines/eevee_next/shaders/eevee_attributes_lib.glsl + engines/eevee_next/shaders/eevee_camera_lib.glsl engines/eevee_next/shaders/eevee_geom_curves_vert.glsl engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl @@ -422,8 +424,8 @@ set(GLSL_SRC intern/shaders/common_subdiv_vbo_lnor_comp.glsl intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl - intern/draw_shader_shared.h intern/draw_common_shader_shared.h + intern/draw_shader_shared.h engines/gpencil/shaders/gpencil_frag.glsl engines/gpencil/shaders/gpencil_vert.glsl diff --git a/source/blender/draw/DRW_select_buffer.h b/source/blender/draw/DRW_select_buffer.h index 324ebebfbe6..d15ec8bed56 100644 --- a/source/blender/draw/DRW_select_buffer.h +++ b/source/blender/draw/DRW_select_buffer.h @@ -9,6 +9,10 @@ #include "BLI_sys_types.h" /* for bool and uint */ +#ifdef __cplusplus +extern "C" { +#endif + struct ARegion; struct Base; struct Depsgraph; @@ -133,3 +137,7 @@ uint DRW_select_buffer_find_nearest_to_point(struct Depsgraph *depsgraph, uint id_max, uint *dist); void DRW_select_buffer_context_create(struct Base **bases, uint bases_len, short select_mode); + +#ifdef __cplusplus +} +#endif
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c index b17efe4b68d..33063e14c03 100644 --- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c +++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c @@ -68,7 +68,7 @@ BLI_INLINE int eevee_cryptomatte_layers_count(const ViewLayer *view_layer) } /* The number of render result passes are needed to store a single cryptomatte layer. Per - * renderpass 2 cryptomatte samples can be stored. */ + * render-pass 2 cryptomatte samples can be stored. */ BLI_INLINE int eevee_cryptomatte_passes_per_layer(const ViewLayer *view_layer) { const int num_cryptomatte_levels = view_layer->cryptomatte_levels; diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c index 4f562dd9804..7f722ff1764 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -95,7 +95,7 @@ typedef struct EEVEE_LightBake { /** Target layer to store the data to. */ int layer; /** Sample count for the convolution. */ - float samples_ct, invsamples_ct; + float samples_count, invsamples_count; /** Sampling bias during convolution step. */ float lod_factor; /** Max cube-map LOD to sample when convolving. */ @@ -282,14 +282,14 @@ static void irradiance_pool_size_get(int visibility_size, int total_samples, int (visibility_size / IRRADIANCE_SAMPLE_SIZE_Y); /* The irradiance itself take one layer, hence the +1 */ - int layer_ct = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER); + int layer_count = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER); - int texel_ct = (int)ceilf((float)total_samples / (float)(layer_ct - 1)); + int texel_count = (int)ceilf((float)total_samples / (float)(layer_count - 1)); r_size[0] = visibility_size * - max_ii(1, min_ii(texel_ct, (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); + max_ii(1, min_ii(texel_count, (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); r_size[1] = visibility_size * - max_ii(1, (texel_ct / (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); - r_size[2] = layer_ct; + max_ii(1, (texel_count / (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); + r_size[2] = layer_count; } static bool EEVEE_lightcache_validate(const LightCache *light_cache, @@ -1118,7 +1118,7 @@ static void eevee_lightbake_render_grid_sample(void *ved, void *user_data) SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex); /* TODO: do this once for the whole bake when we have independent DRWManagers. - * Warning: Some of the things above require this. */ + * WARNING: Some of the things above require this. */ eevee_lightbake_cache_create(vedata, lbake); /* Compute sample position */ diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index 43d0b050cc8..a4bd789438d 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -112,7 +112,7 @@ void EEVEE_lookdev_init(EEVEE_Data *vedata) if (sphere_size != effects->sphere_size || rect->xmax != effects->anchor[0] || rect->ymin != effects->anchor[1]) { - /* Make sphere resolution adaptive to viewport_scale, dpi and lookdev_sphere_size */ + /* Make sphere resolution adaptive to viewport_scale, DPI and #U.lookdev_sphere_size. */ float res_scale = clamp_f( (U.lookdev_sphere_size / 400.0f) * viewport_scale * U.dpi_fac, 0.1f, 1.0f); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index b03a4fa70b4..0a7c8e185c4 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -301,7 +301,7 @@ typedef struct EEVEE_PassList { struct DRWPass *maxz_copydepth_ps; struct DRWPass *maxz_copydepth_layer_ps; - /* Renderpass Accumulation. */ + /* Render-pass Accumulation. */ struct DRWPass *material_accum_ps; struct DRWPass *background_accum_ps; struct DRWPass *cryptomatte_ps; @@ -1069,7 +1069,7 @@ typedef struct EEVEE_PrivateData { GPUTexture *renderpass_col_input; GPUTexture *renderpass_light_input; GPUTexture *renderpass_transmittance_input; - /* Renderpass ubo reference used by material pass. */ + /* Render-pass UBO reference used by material pass. */ struct GPUUniformBuf *renderpass_ubo; /** For rendering shadows. */ struct DRWView *cube_views[6]; diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl index d6eeedd8640..a81d37f4e6f 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl @@ -8,7 +8,7 @@ flat in int slice; -/* Warning: these are not attributes, these are global vars. */ +/* WARNING: these are not attributes, these are global vars. */ vec3 worldPosition = vec3(0.0); vec3 objectPosition = vec3(0.0); vec3 viewPosition = vec3(0.0); @@ -80,8 +80,8 @@ void main() volumeOrco = OrcoTexCoFactors[0].xyz + objectPosition * OrcoTexCoFactors[1].xyz; if (any(lessThan(volumeOrco, vec3(0.0))) || any(greaterThan(volumeOrco, vec3(1.0)))) { - /* Note: Discard is not an explicit return in Metal prior to versions 2.3. - * adding return after discard ensures consistent behaviour and avoids GPU + /* NOTE: Discard is not an explicit return in Metal prior to versions 2.3. + * adding return after discard ensures consistent behavior and avoids GPU * side-effects where control flow continues with undefined values. */ discard; return; diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl index 527bbd18896..3ce54b3122a 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl @@ -70,7 +70,7 @@ void main() vec3 Tr = exp(-s_extinction * s_len); /* integrate along the current step segment */ - /* Note: Original calculation carries precision issues when compiling for AMD GPUs + /* NOTE: Original calculation carries precision issues when compiling for AMD GPUs * and running Metal. This version of the equation retains precision well for all * macOS HW configurations. */ Lscat = (Lscat * (1.0f - Tr)) / max(vec3(1e-8), s_extinction); diff --git a/source/blender/draw/engines/eevee_next/eevee_camera.cc b/source/blender/draw/engines/eevee_next/eevee_camera.cc new file mode 100644 index 00000000000..11cc6453817 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_camera.cc @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#include <array> + +#include "DRW_render.h" + +#include "DNA_camera_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_camera.h" +#include "DEG_depsgraph_query.h" +#include "RE_pipeline.h" + +#include "eevee_camera.hh" +#include "eevee_instance.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Camera + * \{ */ + +void Camera::init() +{ + const Object *camera_eval = inst_.camera_eval_object; + synced_ = false; + data_.swap(); + + CameraData &data = data_.current(); + + if (camera_eval) { + const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data); + switch (cam->type) { + default: + case CAM_PERSP: + data.type = CAMERA_PERSP; + break; + case CAM_ORTHO: + data.type = CAMERA_ORTHO; + break; +#if 0 /* TODO(fclem): Make fisheye properties inside blender. */ + case CAM_PANO: { + switch (cam->panorama_type) { + default: + case CAM_PANO_EQUIRECTANGULAR: + data.type = CAMERA_PANO_EQUIRECT; + break; + case CAM_PANO_FISHEYE_EQUIDISTANT: + data.type = CAMERA_PANO_EQUIDISTANT; + break; + case CAM_PANO_FISHEYE_EQUISOLID: + data.type = CAMERA_PANO_EQUISOLID; + break; + case CAM_PANO_MIRRORBALL: + data.type = CAMERA_PANO_MIRROR; + break; + } + } +#endif + } + } + else if (inst_.drw_view) { + data.type = DRW_view_is_persp_get(inst_.drw_view) ? CAMERA_PERSP : CAMERA_ORTHO; + } + else { + /* Lightprobe baking. */ + data.type = CAMERA_PERSP; + } +} + +void Camera::sync() +{ + const Object *camera_eval = inst_.camera_eval_object; + CameraData &data = data_.current(); + + data.filter_size = inst_.scene->r.gauss; + + if (inst_.drw_view) { + DRW_view_viewmat_get(inst_.drw_view, data.viewmat.ptr(), false); + DRW_view_viewmat_get(inst_.drw_view, data.viewinv.ptr(), true); + DRW_view_winmat_get(inst_.drw_view, data.winmat.ptr(), false); + DRW_view_winmat_get(inst_.drw_view, data.wininv.ptr(), true); + DRW_view_persmat_get(inst_.drw_view, data.persmat.ptr(), false); + DRW_view_persmat_get(inst_.drw_view, data.persinv.ptr(), true); + DRW_view_camtexco_get(inst_.drw_view, data.uv_scale); + } + else if (inst_.render) { + /* TODO(fclem) Overscan */ + // RE_GetCameraWindowWithOverscan(inst_.render->re, g_data->overscan, data.winmat); + RE_GetCameraWindow(inst_.render->re, camera_eval, data.winmat.ptr()); + RE_GetCameraModelMatrix(inst_.render->re, camera_eval, data.viewinv.ptr()); + invert_m4_m4(data.viewmat.ptr(), data.viewinv.ptr()); + invert_m4_m4(data.wininv.ptr(), data.winmat.ptr()); + mul_m4_m4m4(data.persmat.ptr(), data.winmat.ptr(), data.viewmat.ptr()); + invert_m4_m4(data.persinv.ptr(), data.persmat.ptr()); + data.uv_scale = float2(1.0f); + data.uv_bias = float2(0.0f); + } + else { + data.viewmat = float4x4::identity(); + data.viewinv = float4x4::identity(); + perspective_m4(data.winmat.ptr(), -0.1f, 0.1f, -0.1f, 0.1f, 0.1f, 1.0f); + data.wininv = data.winmat.inverted(); + data.persmat = data.winmat * data.viewmat; + data.persinv = data.persmat.inverted(); + } + + if (camera_eval) { + const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data); + data.clip_near = cam->clip_start; + data.clip_far = cam->clip_end; +#if 0 /* TODO(fclem): Make fisheye properties inside blender. */ + data.fisheye_fov = cam->fisheye_fov; + data.fisheye_lens = cam->fisheye_lens; + data.equirect_bias.x = -cam->longitude_min + M_PI_2; + data.equirect_bias.y = -cam->latitude_min + M_PI_2; + data.equirect_scale.x = cam->longitude_min - cam->longitude_max; + data.equirect_scale.y = cam->latitude_min - cam->latitude_max; + /* Combine with uv_scale/bias to avoid doing extra computation. */ + data.equirect_bias += data.uv_bias * data.equirect_scale; + data.equirect_scale *= data.uv_scale; + + data.equirect_scale_inv = 1.0f / data.equirect_scale; +#endif + } + else if (inst_.drw_view) { + data.clip_near = DRW_view_near_distance_get(inst_.drw_view); + data.clip_far = DRW_view_far_distance_get(inst_.drw_view); + data.fisheye_fov = data.fisheye_lens = -1.0f; + data.equirect_bias = float2(0.0f); + data.equirect_scale = float2(0.0f); + } + + data_.current().push_update(); + + synced_ = true; + + /* Detect changes in parameters. */ + if (data_.current() != data_.previous()) { + // inst_.sampling.reset(); + } +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_camera.hh b/source/blender/draw/engines/eevee_next/eevee_camera.hh index 3db343703e0..dfec738b1f3 100644 --- a/source/blender/draw/engines/eevee_next/eevee_camera.hh +++ b/source/blender/draw/engines/eevee_next/eevee_camera.hh @@ -1,11 +1,14 @@ /* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2021 Blender Foundation. - */ + * Copyright 2021 Blender Foundation. */ + +#pragma once /** \file * \ingroup eevee */ +#include "eevee_shader_shared.hh" + namespace blender::eevee { class Instance; @@ -43,4 +46,85 @@ static const float cubeface_mat[6][4][4] = { {0.0f, 0.0f, 0.0f, 1.0f}}, }; +inline void cubeface_winmat_get(float4x4 &winmat, float near, float far) +{ + /* Simple 90° FOV projection. */ + perspective_m4(winmat.ptr(), -near, near, -near, near, near, far); +} + +/* -------------------------------------------------------------------- */ +/** \name CameraData operators + * \{ */ + +inline bool operator==(const CameraData &a, const CameraData &b) +{ + return compare_m4m4(a.persmat.ptr(), b.persmat.ptr(), FLT_MIN) && (a.uv_scale == b.uv_scale) && + (a.uv_bias == b.uv_bias) && (a.equirect_scale == b.equirect_scale) && + (a.equirect_bias == b.equirect_bias) && (a.fisheye_fov == b.fisheye_fov) && + (a.fisheye_lens == b.fisheye_lens) && (a.filter_size == b.filter_size) && + (a.type == b.type); +} + +inline bool operator!=(const CameraData &a, const CameraData &b) +{ + return !(a == b); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Camera + * \{ */ + +/** + * Point of view in the scene. Can be init from viewport or camera object. + */ +class Camera { + private: + Instance &inst_; + + /** Double buffered to detect changes and have history for re-projection. */ + SwapChain<CameraDataBuf, 2> data_; + /** Detects wrong usage. */ + bool synced_ = false; + + public: + Camera(Instance &inst) : inst_(inst){}; + ~Camera(){}; + + void init(); + void sync(); + + /** + * Getters + **/ + const CameraData &data_get() const + { + BLI_assert(synced_); + return data_.current(); + } + const GPUUniformBuf *ubo_get() const + { + return data_.current(); + } + bool is_panoramic() const + { + return eevee::is_panoramic(data_.current().type); + } + bool is_orthographic() const + { + return data_.current().type == CAMERA_ORTHO; + } + const float3 &position() const + { + return *reinterpret_cast<const float3 *>(data_.current().viewinv[3]); + } + const float3 &forward() const + { + return *reinterpret_cast<const float3 *>(data_.current().viewinv[2]); + } +}; + +/** \} */ + } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index 35eb33671db..7141928a20d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -11,9 +11,11 @@ #pragma once -/* Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536. */ -/* Current limiting factor is the sorting phase which is single pass and only sort within a - * threadgroup which maximum size is 1024. */ +/** + Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536. + * Current limiting factor is the sorting phase which is single pass and only sort within a + * thread-group which maximum size is 1024. + */ #define CULLING_BATCH_SIZE 1024 /** diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 922f6c9e1ae..f79b692018f 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -21,9 +21,9 @@ namespace blender::eevee { /* -------------------------------------------------------------------- */ -/** \name Init +/** \name Initialization * - * Init funcions need to be called once at the start of a frame. + * Initialization functions need to be called once at the start of a frame. * Active camera, render extent and enabled render passes are immutable until next init. * This takes care of resizing output buffers and view in case a parameter changed. * IMPORTANT: xxx.init() functions are NOT meant to acquire and allocate DRW resources. @@ -41,9 +41,10 @@ void Instance::init(const int2 &output_res, const View3D *v3d_, const RegionView3D *rv3d_) { - UNUSED_VARS(light_probe_, camera_object_, output_rect); + UNUSED_VARS(light_probe_, output_rect); render = render_; depsgraph = depsgraph_; + camera_orig_object = camera_object_; render_layer = render_layer_; drw_view = drw_view_; v3d = v3d_; @@ -58,9 +59,9 @@ void Instance::update_eval_members() { scene = DEG_get_evaluated_scene(depsgraph); view_layer = DEG_get_evaluated_view_layer(depsgraph); - // camera_eval_object = (camera_orig_object) ? - // DEG_get_evaluated_object(depsgraph, camera_orig_object) : - // nullptr; + camera_eval_object = (camera_orig_object) ? + DEG_get_evaluated_object(depsgraph, camera_orig_object) : + nullptr; } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index c3cf08c8390..47fa005d3d0 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -42,6 +42,8 @@ class Instance { /** Evaluated IDs. */ Scene *scene; ViewLayer *view_layer; + Object *camera_eval_object; + Object *camera_orig_object; /** Only available when rendering for final render. */ const RenderLayer *render_layer; RenderEngine *render; diff --git a/source/blender/draw/engines/eevee_next/eevee_material.cc b/source/blender/draw/engines/eevee_next/eevee_material.cc index 7452e5c26a4..5d4e2d7752d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.cc +++ b/source/blender/draw/engines/eevee_next/eevee_material.cc @@ -281,9 +281,7 @@ Material &MaterialModule::material_sync(::Material *blender_mat, eMaterialGeomet if (ob->type == OB_VOLUME) { return BKE_material_default_volume(); } - else { - return BKE_material_default_surface(); - } + return BKE_material_default_surface(); } return ma; } diff --git a/source/blender/draw/engines/eevee_next/eevee_material.hh b/source/blender/draw/engines/eevee_next/eevee_material.hh index 56f9b077f7a..af9ff6bf6ba 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.hh +++ b/source/blender/draw/engines/eevee_next/eevee_material.hh @@ -104,7 +104,7 @@ static inline eMaterialGeometry to_material_geometry(const Object *ob) } } -/** Unique key to identify each material in the hashmap. */ +/** Unique key to identify each material in the hash-map. */ struct MaterialKey { Material *mat; uint64_t options; @@ -169,7 +169,7 @@ struct ShaderKey { /** \} */ /* -------------------------------------------------------------------- */ -/** \name Default Material Nodetree +/** \name Default Material Node-Tree * * In order to support materials without nodetree we reuse and configure a standalone nodetree that * we pass for shader generation. The GPUMaterial is still stored inside the Material even if @@ -254,4 +254,4 @@ class MaterialModule { /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 086c5f9f358..9efb7a70c7e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -25,7 +25,7 @@ ShaderModule *ShaderModule::g_shader_module = nullptr; ShaderModule *ShaderModule::module_get() { if (g_shader_module == nullptr) { - /* TODO(fclem) threadsafety. */ + /* TODO(@fclem) thread-safety. */ g_shader_module = new ShaderModule(); } return g_shader_module; @@ -34,7 +34,7 @@ ShaderModule *ShaderModule::module_get() void ShaderModule::module_free() { if (g_shader_module != nullptr) { - /* TODO(fclem) threadsafety. */ + /* TODO(@fclem) thread-safety. */ delete g_shader_module; g_shader_module = nullptr; } @@ -148,7 +148,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu /** Noop. */ break; case MAT_GEOM_CURVES: - /** Hair attributes comme from sampler buffer. Transfer attributes to sampler. */ + /** Hair attributes come from sampler buffer. Transfer attributes to sampler. */ for (auto &input : info.vertex_inputs_) { if (input.name == "orco") { /** NOTE: Orco is generated from strand position for now. */ @@ -163,14 +163,14 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu case MAT_GEOM_WORLD: /** * Only orco layer is supported by world and it is procedurally generated. These are here to - * make the attribs_load function calls valids. + * make the attribs_load function calls valid. */ ATTR_FALLTHROUGH; case MAT_GEOM_GPENCIL: /** * Only one uv and one color attribute layer are supported by gpencil objects and they are * already declared in another createInfo. These are here to make the attribs_load - * function calls valids. + * function calls valid. */ for (auto &input : info.vertex_inputs_) { global_vars << input.type << " " << input.name << ";\n"; @@ -190,7 +190,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu const StageInterfaceInfo &iface = *info.vertex_out_interfaces_.first(); /* Globals the attrib_load() can write to when it is in the fragment shader. */ global_vars << "struct " << iface.name << " {\n"; - for (auto &inout : iface.inouts) { + for (const auto &inout : iface.inouts) { global_vars << " " << inout.type << " " << inout.name << ";\n"; } global_vars << "};\n"; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index ba7c97b3b6a..29fcbafb167 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -36,7 +36,7 @@ class ShaderModule { private: std::array<GPUShader *, MAX_SHADER_TYPE> shaders_; - /** Shared shader module accross all engine instances. */ + /** Shared shader module across all engine instances. */ static ShaderModule *g_shader_module; public: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 2225ccac43a..97fc9c5a547 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -19,6 +19,7 @@ namespace blender::eevee { using draw::Framebuffer; +using draw::SwapChain; using draw::Texture; using draw::TextureFromPool; @@ -27,7 +28,53 @@ using draw::TextureFromPool; #define UBO_MIN_MAX_SUPPORTED_SIZE 1 << 14 /* -------------------------------------------------------------------- */ -/** \name Raytracing +/** \name Camera + * \{ */ + +enum eCameraType : uint32_t { + CAMERA_PERSP = 0u, + CAMERA_ORTHO = 1u, + CAMERA_PANO_EQUIRECT = 2u, + CAMERA_PANO_EQUISOLID = 3u, + CAMERA_PANO_EQUIDISTANT = 4u, + CAMERA_PANO_MIRROR = 5u +}; + +static inline bool is_panoramic(eCameraType type) +{ + return type > CAMERA_ORTHO; +} + +struct CameraData { + /* View Matrices of the camera, not from any view! */ + float4x4 persmat; + float4x4 persinv; + float4x4 viewmat; + float4x4 viewinv; + float4x4 winmat; + float4x4 wininv; + /** Camera UV scale and bias. Also known as viewcamtexcofac. */ + float2 uv_scale; + float2 uv_bias; + /** Panorama parameters. */ + float2 equirect_scale; + float2 equirect_scale_inv; + float2 equirect_bias; + float fisheye_fov; + float fisheye_lens; + /** Clipping distances. */ + float clip_near; + float clip_far; + /** Film pixel filter radius. */ + float filter_size; + eCameraType type; +}; +BLI_STATIC_ASSERT_ALIGN(CameraData, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Ray-Tracing * \{ */ enum eClosureBits : uint32_t { @@ -83,5 +130,7 @@ float4 utility_tx_sample(sampler2DArray util_tx, float2 uv, float layer) #ifdef __cplusplus +using CameraDataBuf = draw::UniformBuffer<CameraData>; + } // namespace blender::eevee #endif diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.hh b/source/blender/draw/engines/eevee_next/eevee_sync.hh index 51e0f86fe5c..34357193d3e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.hh +++ b/source/blender/draw/engines/eevee_next/eevee_sync.hh @@ -27,7 +27,7 @@ class Instance; /* -------------------------------------------------------------------- */ /** \name ObjectKey * - * Unique key to identify each object in the hashmap. + * Unique key to identify each object in the hash-map. * \{ */ struct ObjectKey { diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index df45200c712..de7341f814b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -9,7 +9,7 @@ * - The entire main view. * - A fragment of the main view (for panoramic projections). * - A shadow map view. - * - A lightprobe view (either planar, cubemap, irradiance grid). + * - A light-probe view (either planar, cube-map, irradiance grid). * * A pass is a container for scene data. It is view agnostic but has specific logic depending on * its type. Passes are shared between views. @@ -40,7 +40,7 @@ void ShadingView::sync(int2 render_extent_) int64_t render_pixel_count = render_extent_.x * (int64_t)render_extent_.y; /* Divide pixel count between the 6 views. Rendering to a square target. */ extent_[0] = extent_[1] = ceilf(sqrtf(1 + (render_pixel_count / 6))); - /* TODO(fclem) Clip unused views heres. */ + /* TODO(@fclem): Clip unused views here. */ is_enabled_ = true; } else { @@ -60,8 +60,8 @@ void ShadingView::sync(int2 render_extent_) const float(*viewmat_p)[4] = viewmat.ptr(), (*winmat_p)[4] = winmat.ptr(); #if 0 if (false /* inst_.camera.is_panoramic() */) { - /* TODO(fclem) Overscans. */ - /* For now a mandatory 5% overscan for DoF. */ + /* TODO(@fclem) Over-scans. */ + /* For now a mandatory 5% over-scan for DoF. */ float side = data.clip_near * 1.05f; float near = data.clip_near; float far = data.clip_far; @@ -205,4 +205,4 @@ void ShadingView::update_view() /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_view.hh b/source/blender/draw/engines/eevee_next/eevee_view.hh index ab7b5722de1..e78a3222d8b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.hh +++ b/source/blender/draw/engines/eevee_next/eevee_view.hh @@ -8,7 +8,7 @@ * A view is either: * - The entire main view. * - A portion of the main view (for panoramic projections). - * - A lightprobe view (either planar, cubemap, irradiance grid). + * - A light-probe view (either planar, cube-map, irradiance grid). * * A pass is a container for scene data. It is view agnostic but has specific logic depending on * its type. Passes are shared between views. @@ -154,4 +154,4 @@ class MainView { /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_world.cc b/source/blender/draw/engines/eevee_next/eevee_world.cc index 939f6087137..b9cb24fe30a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_world.cc +++ b/source/blender/draw/engines/eevee_next/eevee_world.cc @@ -64,19 +64,17 @@ void World::sync() // } ::World *bl_world = inst_.scene->world; - if (bl_world == nullptr) { // bl_world = BKE_world_default(); return; } - else { - WorldHandle &wo_handle = inst_.sync.sync_world(bl_world); - if (wo_handle.recalc != 0) { - // inst_.lightprobes.set_world_dirty(); - } - wo_handle.reset_recalc_flag(); + WorldHandle &wo_handle = inst_.sync.sync_world(bl_world); + + if (wo_handle.recalc != 0) { + // inst_.lightprobes.set_world_dirty(); } + wo_handle.reset_recalc_flag(); /* TODO(fclem) This should be detected to scene level. */ ::World *orig_world = (::World *)DEG_get_original_id(&bl_world->id); diff --git a/source/blender/draw/engines/eevee_next/eevee_world.hh b/source/blender/draw/engines/eevee_next/eevee_world.hh index 56554051eea..05177928436 100644 --- a/source/blender/draw/engines/eevee_next/eevee_world.hh +++ b/source/blender/draw/engines/eevee_next/eevee_world.hh @@ -18,7 +18,7 @@ namespace blender::eevee { class Instance; /* -------------------------------------------------------------------- */ -/** \name Default World Nodetree +/** \name Default World Node-Tree * * In order to support worlds without nodetree we reuse and configure a standalone nodetree that * we pass for shader generation. The GPUMaterial is still stored inside the World even if diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl new file mode 100644 index 00000000000..f79e9102d76 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl @@ -0,0 +1,166 @@ + +/** + * Camera projection / uv functions and utils. + **/ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +/* -------------------------------------------------------------------- */ +/** \name Panoramic Projections + * + * Adapted from Cycles to match EEVEE's coordinate system. + * \{ */ + +vec2 camera_equirectangular_from_direction(CameraData cam, vec3 dir) +{ + float phi = atan(-dir.z, dir.x); + float theta = acos(dir.y / length(dir)); + return (vec2(phi, theta) - cam.equirect_bias) * cam.equirect_scale_inv; +} + +vec3 camera_equirectangular_to_direction(CameraData cam, vec2 uv) +{ + uv = uv * cam.equirect_scale + cam.equirect_bias; + float phi = uv.x; + float theta = uv.y; + float sin_theta = sin(theta); + return vec3(sin_theta * cos(phi), cos(theta), -sin_theta * sin(phi)); +} + +vec2 camera_fisheye_from_direction(CameraData cam, vec3 dir) +{ + float r = atan(length(dir.xy), -dir.z) / cam.fisheye_fov; + float phi = atan(dir.y, dir.x); + vec2 uv = r * vec2(cos(phi), sin(phi)) + 0.5; + return (uv - cam.uv_bias) / cam.uv_scale; +} + +vec3 camera_fisheye_to_direction(CameraData cam, vec2 uv) +{ + uv = uv * cam.uv_scale + cam.uv_bias; + uv = (uv - 0.5) * 2.0; + float r = length(uv); + if (r > 1.0) { + return vec3(0.0); + } + float phi = safe_acos(uv.x * safe_rcp(r)); + float theta = r * cam.fisheye_fov * 0.5; + if (uv.y < 0.0) { + phi = -phi; + } + return vec3(cos(phi) * sin(theta), sin(phi) * sin(theta), -cos(theta)); +} + +vec2 camera_mirror_ball_from_direction(CameraData cam, vec3 dir) +{ + dir = normalize(dir); + dir.z -= 1.0; + dir *= safe_rcp(2.0 * safe_sqrt(-0.5 * dir.z)); + vec2 uv = 0.5 * dir.xy + 0.5; + return (uv - cam.uv_bias) / cam.uv_scale; +} + +vec3 camera_mirror_ball_to_direction(CameraData cam, vec2 uv) +{ + uv = uv * cam.uv_scale + cam.uv_bias; + vec3 dir; + dir.xy = uv * 2.0 - 1.0; + if (len_squared(dir.xy) > 1.0) { + return vec3(0.0); + } + dir.z = -safe_sqrt(1.0 - sqr(dir.x) - sqr(dir.y)); + const vec3 I = vec3(0.0, 0.0, 1.0); + return reflect(I, dir); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Regular projections + * \{ */ + +vec3 camera_view_from_uv(mat4 projmat, vec2 uv) +{ + return project_point(projmat, vec3(uv * 2.0 - 1.0, 0.0)); +} + +vec2 camera_uv_from_view(mat4 projmat, bool is_persp, vec3 vV) +{ + vec4 tmp = projmat * vec4(vV, 1.0); + if (is_persp && tmp.w <= 0.0) { + /* Return invalid coordinates for points behind the camera. + * This can happen with panoramic projections. */ + return vec2(-1.0); + } + return (tmp.xy / tmp.w) * 0.5 + 0.5; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name General functions handling all projections + * \{ */ + +vec3 camera_view_from_uv(CameraData cam, vec2 uv) +{ + vec3 vV; + switch (cam.type) { + default: + case CAMERA_ORTHO: + case CAMERA_PERSP: + return camera_view_from_uv(cam.wininv, uv); + case CAMERA_PANO_EQUIRECT: + vV = camera_equirectangular_to_direction(cam, uv); + break; + case CAMERA_PANO_EQUIDISTANT: + /* ATTR_FALLTHROUGH; */ + case CAMERA_PANO_EQUISOLID: + vV = camera_fisheye_to_direction(cam, uv); + break; + case CAMERA_PANO_MIRROR: + vV = camera_mirror_ball_to_direction(cam, uv); + break; + } + return vV; +} + +vec2 camera_uv_from_view(CameraData cam, vec3 vV) +{ + switch (cam.type) { + default: + case CAMERA_ORTHO: + return camera_uv_from_view(cam.winmat, false, vV); + case CAMERA_PERSP: + return camera_uv_from_view(cam.winmat, true, vV); + case CAMERA_PANO_EQUIRECT: + return camera_equirectangular_from_direction(cam, vV); + case CAMERA_PANO_EQUISOLID: + /* ATTR_FALLTHROUGH; */ + case CAMERA_PANO_EQUIDISTANT: + return camera_fisheye_from_direction(cam, vV); + case CAMERA_PANO_MIRROR: + return camera_mirror_ball_from_direction(cam, vV); + } +} + +vec2 camera_uv_from_world(CameraData cam, vec3 V) +{ + vec3 vV = transform_point(cam.viewmat, V); + switch (cam.type) { + default: + case CAMERA_ORTHO: + return camera_uv_from_view(cam.persmat, false, V); + case CAMERA_PERSP: + return camera_uv_from_view(cam.persmat, true, V); + case CAMERA_PANO_EQUIRECT: + return camera_equirectangular_from_direction(cam, vV); + case CAMERA_PANO_EQUISOLID: + /* ATTR_FALLTHROUGH; */ + case CAMERA_PANO_EQUIDISTANT: + return camera_fisheye_from_direction(cam, vV); + case CAMERA_PANO_MIRROR: + return camera_mirror_ball_from_direction(cam, vV); + } +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index 4d6895bcde0..12b8e085455 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -66,7 +66,7 @@ GPU_SHADER_INTERFACE_INFO(eevee_surf_iface, "interp") GPU_SHADER_CREATE_INFO(eevee_surf_deferred) .vertex_out(eevee_surf_iface) - /* Note: This removes the possibility of using gl_FragDepth. */ + /* NOTE: This removes the possibility of using gl_FragDepth. */ // .early_fragment_test(true) /* Direct output. */ .fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0) @@ -95,7 +95,7 @@ GPU_SHADER_CREATE_INFO(eevee_surf_forward) .fragment_source("eevee_surf_forward_frag.glsl") // .additional_info("eevee_sampling_data", // "eevee_lightprobe_data", - /* Optionnally added depending on the material. */ + /* Optionally added depending on the material. */ // "eevee_raytrace_data", // "eevee_transmittance_data", // "eevee_utility_texture", diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c index 40ebd262df5..ec44fdf42d5 100644 --- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c @@ -290,7 +290,7 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, /* Masking: Go through mask list and extract valid masks in a bitmap. */ if (is_masked) { bool valid_mask = false; - /* Warning: only GP_MAX_MASKBITS amount of bits. + /* WARNING: only #GP_MAX_MASKBITS amount of bits. * TODO(fclem): Find a better system without any limitation. */ tgp_layer->mask_bits = BLI_memblock_alloc(pd->gp_maskbit_pool); tgp_layer->mask_invert_bits = BLI_memblock_alloc(pd->gp_maskbit_pool); diff --git a/source/blender/draw/engines/gpencil/gpencil_render.c b/source/blender/draw/engines/gpencil/gpencil_render.c index 19afdb3de5a..c7ef8677336 100644 --- a/source/blender/draw/engines/gpencil/gpencil_render.c +++ b/source/blender/draw/engines/gpencil/gpencil_render.c @@ -61,10 +61,10 @@ void GPENCIL_render_init(GPENCIL_Data *vedata, /* Depth need to be remapped to [0..1] range. */ pix_z = MEM_dupallocN(pix_z); - int pix_ct = rpass_z_src->rectx * rpass_z_src->recty; + int pix_num = rpass_z_src->rectx * rpass_z_src->recty; if (DRW_view_is_persp_get(view)) { - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { pix_z[i] = (-winmat[3][2] / -pix_z[i]) - winmat[2][2]; pix_z[i] = clamp_f(pix_z[i] * 0.5f + 0.5f, 0.0f, 1.0f); } @@ -74,7 +74,7 @@ void GPENCIL_render_init(GPENCIL_Data *vedata, float near = DRW_view_near_distance_get(view); float far = DRW_view_far_distance_get(view); float range_inv = 1.0f / fabsf(far - near); - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { pix_z[i] = (pix_z[i] + near) * range_inv; pix_z[i] = clamp_f(pix_z[i], 0.0f, 1.0f); } @@ -172,11 +172,11 @@ static void GPENCIL_render_result_z(struct RenderLayer *rl, float winmat[4][4]; DRW_view_winmat_get(NULL, winmat, false); - int pix_ct = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); + int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); /* Convert GPU depth [0..1] to view Z [near..far] */ if (DRW_view_is_persp_get(NULL)) { - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } @@ -192,7 +192,7 @@ static void GPENCIL_render_result_z(struct RenderLayer *rl, float far = DRW_view_far_distance_get(NULL); float range = fabsf(far - near); - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.c index 5c5226bfe65..9531b0dd983 100644 --- a/source/blender/draw/engines/overlay/overlay_gpencil.c +++ b/source/blender/draw/engines/overlay/overlay_gpencil.c @@ -124,7 +124,7 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata) } } - /* Handles and curve point for Curve Edit submode. */ + /* Handles and curve point for Curve Edit sub-mode. */ if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) { DRWState state = DRW_STATE_WRITE_COLOR; DRW_PASS_CREATE(psl->edit_gpencil_curve_ps, state | pd->clipping_state); @@ -297,7 +297,7 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) } const int gridlines = (gpd->grid.lines <= 0) ? 1 : gpd->grid.lines; - int line_ct = gridlines * 4 + 2; + const int line_count = gridlines * 4 + 2; DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA; state |= (grid_xray) ? DRW_STATE_DEPTH_ALWAYS : DRW_STATE_DEPTH_LESS_EQUAL; @@ -311,8 +311,8 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) DRW_shgroup_uniform_vec3_copy(grp, "xAxis", mat[0]); DRW_shgroup_uniform_vec3_copy(grp, "yAxis", mat[1]); DRW_shgroup_uniform_vec3_copy(grp, "origin", mat[3]); - DRW_shgroup_uniform_int_copy(grp, "halfLineCount", line_ct / 2); - DRW_shgroup_call_procedural_lines(grp, NULL, line_ct); + DRW_shgroup_uniform_int_copy(grp, "halfLineCount", line_count / 2); + DRW_shgroup_call_procedural_lines(grp, NULL, line_count); } } diff --git a/source/blender/draw/engines/overlay/shaders/infos/extra_info.hh b/source/blender/draw/engines/overlay/shaders/infos/extra_info.hh index a765d881682..b9b1b73dbd4 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/extra_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/extra_info.hh @@ -284,7 +284,7 @@ GPU_SHADER_INTERFACE_INFO(overlay_particle_iface, "").flat(Type::VEC4, "finalCol GPU_SHADER_CREATE_INFO(overlay_particle) .sampler(0, ImageType::FLOAT_1D, "weightTex") - .push_constant(Type::VEC4, "color") /* Drawsize packed in alpha */ + .push_constant(Type::VEC4, "color") /* Draw-size packed in alpha. */ .vertex_in(0, Type::VEC3, "part_pos") .vertex_in(1, Type::VEC4, "part_rot") .vertex_in(2, Type::FLOAT, "part_val") diff --git a/source/blender/draw/engines/overlay/shaders/infos/volume_info.hh b/source/blender/draw/engines/overlay/shaders/infos/volume_info.hh index 713c8c2dc4b..5853e974eeb 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/volume_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/volume_info.hh @@ -48,7 +48,7 @@ GPU_SHADER_CREATE_INFO(overlay_volume_velocity_needle) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Volume Gridlines +/** \name Volume Grid-Lines * \{ */ GPU_SHADER_INTERFACE_INFO(overlay_volume_gridlines_iface, "").flat(Type::VEC4, "finalColor"); diff --git a/source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh b/source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh index 43367121d6a..16b59f6bb7d 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh @@ -21,7 +21,7 @@ GPU_SHADER_CREATE_INFO(overlay_wireframe) .sampler(0, ImageType::DEPTH_2D, "depthTex") .vertex_in(0, Type::VEC3, "pos") .vertex_in(1, Type::VEC3, "nor") - .vertex_in(2, Type::FLOAT, "wd") /* wiredata */ + .vertex_in(2, Type::FLOAT, "wd") /* wire-data. */ .vertex_out(overlay_wireframe_iface) .vertex_source("wireframe_vert.glsl") .fragment_source("wireframe_frag.glsl") diff --git a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl index c33bb474d96..3e3c0e4e89f 100644 --- a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl @@ -347,7 +347,7 @@ void main() line_end = vec2(0.0, 0.5); break; default: - /* Ensure values are assigned to, avoids undefined behaviour for + /* Ensure values are assigned to, avoids undefined behavior for * divergent control-flow. This can occur if discard is called * as discard is not treated as a return in Metal 2.2. So * side-effects can still cause problems. */ diff --git a/source/blender/draw/engines/select/shaders/infos/select_id_info.hh b/source/blender/draw/engines/select/shaders/infos/select_id_info.hh index ad0de61ffc3..e3166582197 100644 --- a/source/blender/draw/engines/select/shaders/infos/select_id_info.hh +++ b/source/blender/draw/engines/select/shaders/infos/select_id_info.hh @@ -3,7 +3,7 @@ #include "gpu_shader_create_info.hh" /* -------------------------------------------------------------------- */ -/** \name Select ID fo Edit Mesh selection +/** \name Select ID for Edit Mesh Selection * \{ */ GPU_SHADER_INTERFACE_INFO(select_id_iface, "").flat(Type::INT, "id"); diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh index e2a54788c72..735e7b6d867 100644 --- a/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh +++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh @@ -91,7 +91,7 @@ GPU_SHADER_CREATE_INFO(workbench_material) * \{ */ GPU_SHADER_CREATE_INFO(workbench_transparent_accum) - /* Note: Blending will be skipped on objectId because output is a + /* NOTE: Blending will be skipped on objectId because output is a * non-normalized integer buffer. */ .fragment_out(0, Type::VEC4, "transparentAccum") .fragment_out(1, Type::VEC4, "revealageAccum") diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl index 36059b6076f..49e26cd3e0c 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl @@ -218,13 +218,13 @@ void main() /* Manual depth test. TODO: remove. */ float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r; if (gl_FragCoord.z >= depth) { - /* Note: In the Metal API, prior to Metal 2.3, Discard is not an explicit return and can - * produce undefined behaviour. This is especially prominent with derivatives if control-flow + /* NOTE: In the Metal API, prior to Metal 2.3, Discard is not an explicit return and can + * produce undefined behavior. This is especially prominent with derivatives if control-flow * divergence is present. * - * Adding a return call eliminates undefined behaviour and a later out-of-bounds read causing + * Adding a return call eliminates undefined behavior and a later out-of-bounds read causing * a crash on AMD platforms. - * This behaviour can also affect OpenGL on certain devices. */ + * This behavior can also affect OpenGL on certain devices. */ discard; return; } diff --git a/source/blender/draw/engines/workbench/workbench_render.c b/source/blender/draw/engines/workbench/workbench_render.c index 1279682e899..e5dcf6c5624 100644 --- a/source/blender/draw/engines/workbench/workbench_render.c +++ b/source/blender/draw/engines/workbench/workbench_render.c @@ -115,11 +115,11 @@ static void workbench_render_result_z(struct RenderLayer *rl, float winmat[4][4]; DRW_view_winmat_get(NULL, winmat, false); - int pix_ct = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); + int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); /* Convert ogl depth [0..1] to view Z [near..far] */ if (DRW_view_is_persp_get(NULL)) { - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } @@ -135,7 +135,7 @@ static void workbench_render_result_z(struct RenderLayer *rl, float far = DRW_view_far_distance_get(NULL); float range = fabsf(far - near); - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c index 2c902e9b627..ce7773e7439 100644 --- a/source/blender/draw/engines/workbench/workbench_volume.c +++ b/source/blender/draw/engines/workbench/workbench_volume.c @@ -124,12 +124,12 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata, double noise_ofs; BLI_halton_1d(3, 0.0, wpd->taa_sample, &noise_ofs); float dim[3], step_length, max_slice; - float slice_ct[3] = {fds->res[0], fds->res[1], fds->res[2]}; - mul_v3_fl(slice_ct, max_ff(0.001f, fds->slice_per_voxel)); - max_slice = max_fff(slice_ct[0], slice_ct[1], slice_ct[2]); + float slice_count[3] = {fds->res[0], fds->res[1], fds->res[2]}; + mul_v3_fl(slice_count, max_ff(0.001f, fds->slice_per_voxel)); + max_slice = max_fff(slice_count[0], slice_count[1], slice_count[2]); BKE_object_dimensions_get(ob, dim); - invert_v3(slice_ct); - mul_v3_v3(dim, slice_ct); + invert_v3(slice_count); + mul_v3_v3(dim, slice_count); step_length = len_v3(dim); grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); @@ -273,12 +273,12 @@ static void workbench_volume_object_cache_populate(WORKBENCH_Data *vedata, float step_length, max_slice; int resolution[3]; GPU_texture_get_mipmap_size(grid->texture, 0, resolution); - float slice_ct[3] = {resolution[0], resolution[1], resolution[2]}; - mul_v3_fl(slice_ct, max_ff(0.001f, 5.0f)); - max_slice = max_fff(slice_ct[0], slice_ct[1], slice_ct[2]); - invert_v3(slice_ct); - mul_v3_v3(slice_ct, world_size); - step_length = len_v3(slice_ct); + float slice_count[3] = {resolution[0], resolution[1], resolution[2]}; + mul_v3_fl(slice_count, max_ff(0.001f, 5.0f)); + max_slice = max_fff(slice_count[0], slice_count[1], slice_count[2]); + invert_v3(slice_count); + mul_v3_v3(slice_count, world_size); + step_length = len_v3(slice_count); /* Set uniforms. */ grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index d7e752a43f4..366dd40c220 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -31,6 +31,9 @@ * discarding all data inside it. * Data can be accessed using the [] operator. * + * `draw::StorageFlexibleBuffer<T>` + * Same as StorageArrayBuffer but will auto resize on access when using the [] operator. + * * `draw::StorageBuffer<T>` * A storage buffer object class inheriting from T. * Data can be accessed just like a normal T object. @@ -340,6 +343,38 @@ template< typename T, /** True if created on device and no memory host memory is allocated. */ bool device_only = false> +class StorageFlexibleBuffer : public detail::StorageCommon<T, 1, device_only> { + public: + StorageFlexibleBuffer(const char *name = nullptr) + : detail::StorageCommon<T, 1, device_only>(name) + { + /* TODO(@fclem): We should map memory instead. */ + this->data_ = (T *)MEM_mallocN_aligned(sizeof(T), 16, this->name_); + } + ~StorageFlexibleBuffer() + { + MEM_freeN(this->data_); + } + + /* Resize on access. */ + T &operator[](int64_t index) + { + BLI_STATIC_ASSERT(!device_only, ""); + BLI_assert(index >= 0); + if (index >= this->len_) { + this->resize(this->len_ * 2); + } + return this->data_[index]; + } + + /* TODO(fclem): Implement shrinking. */ +}; + +template< + /** Type of the values stored in this uniform buffer. */ + typename T, + /** True if created on device and no memory host memory is allocated. */ + bool device_only = false> class StorageBuffer : public T, public detail::StorageCommon<T, 1, device_only> { public: StorageBuffer(const char *name = nullptr) : detail::StorageCommon<T, 1, device_only>(name) @@ -880,4 +915,56 @@ class Framebuffer : NonCopyable { /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Double & Triple buffering util + * + * This is not strictly related to a GPU type and could be moved elsewhere. + * \{ */ + +template<typename T, int64_t len> class SwapChain { + private: + std::array<T, len> chain_; + int64_t index_ = 0; + + public: + void swap() + { + index_ = (index_ + 1) % len; + } + + T ¤t() + { + return chain_[index_]; + } + + T &previous() + { + /* Avoid modulo operation with negative numbers. */ + return chain_[(index_ + len - 1) % len]; + } + + T &next() + { + return chain_[(index_ + 1) % len]; + } + + const T ¤t() const + { + return chain_[index_]; + } + + const T &previous() const + { + /* Avoid modulo operation with negative numbers. */ + return chain_[(index_ + len - 1) % len]; + } + + const T &next() const + { + return chain_[(index_ + 1) % len]; + } +}; + +/** \} */ + } // namespace blender::draw diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 712118e8282..7d2b984d205 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -296,6 +296,8 @@ void DRW_shader_library_free(DRWShaderLibrary *lib); * therefore they aren't ordered as a bit mask. */ typedef enum { + /** To be used for compute passes. */ + DRW_STATE_NO_DRAW = 0, /** Write mask */ DRW_STATE_WRITE_DEPTH = (1 << 0), DRW_STATE_WRITE_COLOR = (1 << 1), @@ -430,12 +432,12 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup, DRW_shgroup_call_ex(shgroup, ob, NULL, geom, true, NULL) void DRW_shgroup_call_range( - DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint v_sta, uint v_ct); + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint v_sta, uint v_num); /** * A count of 0 instance will use the default number of instance in the batch. */ void DRW_shgroup_call_instance_range( - DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct); + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_num); void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, int groups_x_len, @@ -621,6 +623,12 @@ void DRW_shgroup_vertex_buffer_ex(DRWShadingGroup *shgroup, void DRW_shgroup_vertex_buffer_ref_ex(DRWShadingGroup *shgroup, const char *name, struct GPUVertBuf **vertex_buffer DRW_DEBUG_FILE_LINE_ARGS); +void DRW_shgroup_buffer_texture_ex(DRWShadingGroup *shgroup, + const char *name, + struct GPUVertBuf *vertex_buffer); +void DRW_shgroup_buffer_texture_ref_ex(DRWShadingGroup *shgroup, + const char *name, + struct GPUVertBuf **vertex_buffer); #ifdef DRW_UNUSED_RESOURCE_TRACKING # define DRW_shgroup_vertex_buffer(shgroup, name, vert) \ @@ -738,6 +746,7 @@ const DRWView *DRW_view_get_active(void); */ void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len); void DRW_view_camtexco_set(DRWView *view, float texco[4]); +void DRW_view_camtexco_get(const DRWView *view, float r_texco[4]); /* For all getters, if view is NULL, default view is assumed. */ diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index 7b8f34b999c..ebcdabe4942 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -108,7 +108,7 @@ static void curve_eval_render_wire_verts_edges_len_get(const blender::bke::Curve const blender::VArray<bool> cyclic = curves.cyclic(); for (const int i : curves.curves_range()) { const IndexRange points = curves.evaluated_points_for_curve(i); - *r_edge_len += blender::bke::curves::curve_segment_size(points.size(), cyclic[i]); + *r_edge_len += blender::bke::curves::curve_segment_num(points.size(), cyclic[i]); } } diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index f2742f3bcc7..1896df7c650 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -37,6 +37,7 @@ using blender::float3; using blender::IndexRange; +using blender::MutableSpan; using blender::Span; /* ---------------------------------------------------------------------- */ @@ -147,46 +148,50 @@ static void ensure_seg_pt_count(const Curves &curves, CurvesEvalCache &curves_ca return; } - curves_cache.strands_len = curves.geometry.curve_size; - curves_cache.elems_len = curves.geometry.point_size + curves.geometry.curve_size; - curves_cache.point_len = curves.geometry.point_size; + curves_cache.strands_len = curves.geometry.curve_num; + curves_cache.elems_len = curves.geometry.point_num + curves.geometry.curve_num; + curves_cache.point_len = curves.geometry.point_num; } -static void curves_batch_cache_fill_segments_proc_pos(const Curves &curves_id, - GPUVertBufRaw &attr_step, - GPUVertBufRaw &length_step) +struct PositionAndParameter { + float3 position; + float parameter; +}; + +static void curves_batch_cache_fill_segments_proc_pos( + const Curves &curves_id, + MutableSpan<PositionAndParameter> posTime_data, + MutableSpan<float> hairLength_data) { /* TODO: use hair radius layer if available. */ - const int curve_size = curves_id.geometry.curve_size; + const int curve_num = curves_id.geometry.curve_num; const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( curves_id.geometry); Span<float3> positions = curves.positions(); - for (const int i : IndexRange(curve_size)) { - const IndexRange curve_range = curves.points_for_curve(i); + for (const int i_curve : IndexRange(curve_num)) { + const IndexRange points = curves.points_for_curve(i_curve); + + Span<float3> curve_positions = positions.slice(points); + MutableSpan<PositionAndParameter> curve_posTime_data = posTime_data.slice(points); - Span<float3> curve_positions = positions.slice(curve_range); float total_len = 0.0f; - float *seg_data_first; - for (const int i_curve : curve_positions.index_range()) { - float *seg_data = (float *)GPU_vertbuf_raw_step(&attr_step); - copy_v3_v3(seg_data, curve_positions[i_curve]); - if (i_curve == 0) { - seg_data_first = seg_data; - } - else { - total_len += blender::math::distance(curve_positions[i_curve - 1], - curve_positions[i_curve]); + for (const int i_point : curve_positions.index_range()) { + if (i_point > 0) { + total_len += blender::math::distance(curve_positions[i_point - 1], + curve_positions[i_point]); } - seg_data[3] = total_len; + curve_posTime_data[i_point].position = curve_positions[i_point]; + curve_posTime_data[i_point].parameter = total_len; } + hairLength_data[i_curve] = total_len; + /* Assign length value. */ - *(float *)GPU_vertbuf_raw_step(&length_step) = total_len; if (total_len > 0.0f) { + const float factor = 1.0f / total_len; /* Divide by total length to have a [0-1] number. */ - for ([[maybe_unused]] const int i_curve : curve_positions.index_range()) { - seg_data_first[3] /= total_len; - seg_data_first += 4; + for (const int i_point : curve_positions.index_range()) { + curve_posTime_data[i_point].parameter *= factor; } } } @@ -199,26 +204,26 @@ static void curves_batch_cache_ensure_procedural_pos(Curves &curves, if (cache.proc_point_buf == nullptr || DRW_vbo_requested(cache.proc_point_buf)) { /* Initialize vertex format. */ GPUVertFormat format = {0}; - uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); GPU_vertformat_alias_add(&format, "pos"); cache.proc_point_buf = GPU_vertbuf_create_with_format(&format); GPU_vertbuf_data_alloc(cache.proc_point_buf, cache.point_len); - GPUVertBufRaw point_step; - GPU_vertbuf_attr_get_raw_data(cache.proc_point_buf, pos_id, &point_step); + MutableSpan posTime_data{ + reinterpret_cast<PositionAndParameter *>(GPU_vertbuf_get_data(cache.proc_point_buf)), + cache.point_len}; GPUVertFormat length_format = {0}; - uint length_id = GPU_vertformat_attr_add( - &length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); cache.proc_length_buf = GPU_vertbuf_create_with_format(&length_format); GPU_vertbuf_data_alloc(cache.proc_length_buf, cache.strands_len); - GPUVertBufRaw length_step; - GPU_vertbuf_attr_get_raw_data(cache.proc_length_buf, length_id, &length_step); + MutableSpan hairLength_data{ + reinterpret_cast<float *>(GPU_vertbuf_get_data(cache.proc_length_buf)), cache.strands_len}; - curves_batch_cache_fill_segments_proc_pos(curves, point_step, length_step); + curves_batch_cache_fill_segments_proc_pos(curves, posTime_data, hairLength_data); /* Create vbo immediately to bind to texture buffer. */ GPU_vertbuf_use(cache.proc_point_buf); @@ -307,7 +312,7 @@ static void curves_batch_cache_fill_segments_indices(const Curves &curves, const int res, GPUIndexBufBuilder &elb) { - const int curves_num = curves.geometry.curve_size; + const int curves_num = curves.geometry.curve_num; uint curr_point = 0; diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index dac4b7488be..7fdeaf34965 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -531,7 +531,7 @@ static uint mesh_cd_calc_gpu_layers_vcol_used(const Mesh *me_query, layer_i; } - /* Note: this is not the same as the layer_i below. */ + /* NOTE: this is not the same as the layer_i below. */ if (layer_i != -1) { layer = (domain == ATTR_DOMAIN_POINT ? cd_vdata : cd_ldata)->layers + layer_i; } @@ -544,7 +544,7 @@ static uint mesh_cd_calc_gpu_layers_vcol_used(const Mesh *me_query, return -1; } - /* Note: this is the logical index into the color attribute list, + /* NOTE: this is the logical index into the color attribute list, * not the customdata index. */ int vcol_i = BKE_id_attribute_to_index( (ID *)me_query, layer, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); @@ -701,7 +701,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, break; } - /* Note: attr->type will always be CD_PROP_COLOR even for + /* NOTE: attr->type will always be CD_PROP_COLOR even for * CD_PROP_BYTE_COLOR layers, see node_shader_gpu_vertex_color in * node_shader_vertex_color.cc. */ diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c index a1c0a42ba6f..c1d609bf648 100644 --- a/source/blender/draw/intern/draw_cache_impl_particles.c +++ b/source/blender/draw/intern/draw_cache_impl_particles.c @@ -277,7 +277,7 @@ static void particle_calculate_parent_uvs(ParticleSystem *psys, ParticleSystemModifierData *psmd, const int num_uv_layers, const int parent_index, - /*const*/ MTFace **mtfaces, + const MTFace **mtfaces, float (*r_uv)[2]) { if (psmd == NULL) { @@ -306,7 +306,7 @@ static void particle_calculate_parent_mcol(ParticleSystem *psys, ParticleSystemModifierData *psmd, const int num_col_layers, const int parent_index, - /*const*/ MCol **mcols, + const MCol **mcols, MCol *r_mcol) { if (psmd == NULL) { @@ -337,7 +337,7 @@ static void particle_interpolate_children_uvs(ParticleSystem *psys, ParticleSystemModifierData *psmd, const int num_uv_layers, const int child_index, - /*const*/ MTFace **mtfaces, + const MTFace **mtfaces, float (*r_uv)[2]) { if (psmd == NULL) { @@ -361,7 +361,7 @@ static void particle_interpolate_children_mcol(ParticleSystem *psys, ParticleSystemModifierData *psmd, const int num_col_layers, const int child_index, - /*const*/ MCol **mcols, + const MCol **mcols, MCol *r_mcol) { if (psmd == NULL) { @@ -388,7 +388,7 @@ static void particle_calculate_uvs(ParticleSystem *psys, const int num_uv_layers, const int parent_index, const int child_index, - /*const*/ MTFace **mtfaces, + const MTFace **mtfaces, float (**r_parent_uvs)[2], float (**r_uv)[2]) { @@ -431,7 +431,7 @@ static void particle_calculate_mcol(ParticleSystem *psys, const int num_col_layers, const int parent_index, const int child_index, - /*const*/ MCol **mcols, + const MCol **mcols, MCol **r_parent_mcol, MCol **r_mcol) { @@ -482,8 +482,8 @@ static int particle_batch_cache_fill_segments(ParticleSystem *psys, const int num_path_keys, const int num_uv_layers, const int num_col_layers, - /*const*/ MTFace **mtfaces, - /*const*/ MCol **mcols, + const MTFace **mtfaces, + const MCol **mcols, uint *uv_id, uint *col_id, float (***r_parent_uvs)[2], @@ -713,11 +713,11 @@ static int particle_batch_cache_fill_strands_data(ParticleSystem *psys, GPUVertBufRaw *seg_step, float (***r_parent_uvs)[2], GPUVertBufRaw *uv_step, - MTFace **mtfaces, + const MTFace **mtfaces, int num_uv_layers, MCol ***r_parent_mcol, GPUVertBufRaw *col_step, - MCol **mcols, + const MCol **mcols, int num_col_layers) { const bool is_simple = (psys->part->childtype == PART_CHILD_PARTICLES); @@ -834,8 +834,8 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit GPUVertBufRaw uv_step[MAX_MTFACE]; GPUVertBufRaw col_step[MAX_MCOL]; - MTFace *mtfaces[MAX_MTFACE] = {NULL}; - MCol *mcols[MAX_MCOL] = {NULL}; + const MTFace *mtfaces[MAX_MTFACE] = {NULL}; + const MCol *mcols[MAX_MCOL] = {NULL}; float(**parent_uvs)[2] = NULL; MCol **parent_mcol = NULL; @@ -909,12 +909,13 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit BKE_mesh_tessface_ensure(psmd->mesh_final); if (cache->num_uv_layers) { for (int j = 0; j < cache->num_uv_layers; j++) { - mtfaces[j] = (MTFace *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MTFACE, j); + mtfaces[j] = (const MTFace *)CustomData_get_layer_n( + &psmd->mesh_final->fdata, CD_MTFACE, j); } } if (cache->num_col_layers) { for (int j = 0; j < cache->num_col_layers; j++) { - mcols[j] = (MCol *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MCOL, j); + mcols[j] = (const MCol *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MCOL, j); } } } @@ -930,11 +931,11 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit &seg_step, &parent_uvs, uv_step, - (MTFace **)mtfaces, + mtfaces, cache->num_uv_layers, &parent_mcol, col_step, - (MCol **)mcols, + mcols, cache->num_col_layers); } else { @@ -951,11 +952,11 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit &seg_step, &parent_uvs, uv_step, - (MTFace **)mtfaces, + mtfaces, cache->num_uv_layers, &parent_mcol, col_step, - (MCol **)mcols, + mcols, cache->num_col_layers); } if (psys->childcache) { @@ -970,11 +971,11 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit &seg_step, &parent_uvs, uv_step, - (MTFace **)mtfaces, + mtfaces, cache->num_uv_layers, &parent_mcol, col_step, - (MCol **)mcols, + mcols, cache->num_col_layers); } } @@ -1147,8 +1148,8 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, int num_col_layers = 0; int active_uv = 0; int active_col = 0; - MTFace **mtfaces = NULL; - MCol **mcols = NULL; + const MTFace **mtfaces = NULL; + const MCol **mcols = NULL; float(**parent_uvs)[2] = NULL; MCol **parent_mcol = NULL; @@ -1214,13 +1215,14 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, if (num_uv_layers) { mtfaces = MEM_mallocN(sizeof(*mtfaces) * num_uv_layers, "Faces UV layers"); for (int i = 0; i < num_uv_layers; i++) { - mtfaces[i] = (MTFace *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MTFACE, i); + mtfaces[i] = (const MTFace *)CustomData_get_layer_n( + &psmd->mesh_final->fdata, CD_MTFACE, i); } } if (num_col_layers) { mcols = MEM_mallocN(sizeof(*mcols) * num_col_layers, "Color layers"); for (int i = 0; i < num_col_layers; i++) { - mcols[i] = (MCol *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MCOL, i); + mcols[i] = (const MCol *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MCOL, i); } } } @@ -1304,10 +1306,10 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, MEM_freeN(parent_mcol); } if (num_uv_layers) { - MEM_freeN(mtfaces); + MEM_freeN((void *)mtfaces); } if (num_col_layers) { - MEM_freeN(mcols); + MEM_freeN((void *)mcols); } if (psmd != NULL) { MEM_freeN(uv_id); diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 8eb0509d615..cfdf059ca37 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -745,8 +745,8 @@ struct DRWCacheBuildingContext { /* Origindex layers from the mesh to directly look up during traversal the origindex from the * base mesh for edit data so that we do not have to handle yet another GPU buffer and do this in * the shaders. */ - int *v_origindex; - int *e_origindex; + const int *v_origindex; + const int *e_origindex; }; static bool draw_subdiv_topology_info_cb(const SubdivForeachContext *foreach_context, diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index 779ac43178c..84e79cd8be9 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -44,7 +44,7 @@ float *DRW_color_background_blend_get(int theme_id); bool DRW_object_is_flat(struct Object *ob, int *r_axis); bool DRW_object_axis_orthogonal_to_view(struct Object *ob, int axis); -/* draw_hair.c */ +/* draw_hair.cc */ /** * This creates a shading group with display hairs. diff --git a/source/blender/draw/intern/draw_curves.cc b/source/blender/draw/intern/draw_curves.cc index 88118361115..2edf596ac63 100644 --- a/source/blender/draw/intern/draw_curves.cc +++ b/source/blender/draw/intern/draw_curves.cc @@ -209,7 +209,7 @@ DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object, DRW_shgroup_uniform_texture(shgrp, "ac", g_dummy_texture); /* TODO: Generalize radius implementation for curves data type. */ - float hair_rad_shape = 1.0f; + float hair_rad_shape = 0.0f; float hair_rad_root = 0.005f; float hair_rad_tip = 0.0f; bool hair_close_tip = true; diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.cc index 8351452769d..0a3c16e0d71 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.cc @@ -35,7 +35,7 @@ # define USE_COMPUTE_SHADERS #endif -BLI_INLINE eParticleRefineShaderType drw_hair_shader_type_get(void) +BLI_INLINE eParticleRefineShaderType drw_hair_shader_type_get() { #ifdef USE_COMPUTE_SHADERS if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support()) { @@ -49,21 +49,21 @@ BLI_INLINE eParticleRefineShaderType drw_hair_shader_type_get(void) } #ifndef USE_TRANSFORM_FEEDBACK -typedef struct ParticleRefineCall { +struct ParticleRefineCall { struct ParticleRefineCall *next; GPUVertBuf *vbo; DRWShadingGroup *shgrp; uint vert_len; -} ParticleRefineCall; +}; -static ParticleRefineCall *g_tf_calls = NULL; +static ParticleRefineCall *g_tf_calls = nullptr; static int g_tf_id_offset; static int g_tf_target_width; static int g_tf_target_height; #endif -static GPUVertBuf *g_dummy_vbo = NULL; -static GPUTexture *g_dummy_texture = NULL; +static GPUVertBuf *g_dummy_vbo = nullptr; +static GPUTexture *g_dummy_texture = nullptr; static DRWPass *g_tf_pass; /* XXX can be a problem with multiple DRWManager in the future */ static GPUShader *hair_refine_shader_get(ParticleRefineShader refinement) @@ -74,12 +74,12 @@ static GPUShader *hair_refine_shader_get(ParticleRefineShader refinement) void DRW_hair_init(void) { #if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS) - g_tf_pass = DRW_pass_create("Update Hair Pass", 0); + g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_NO_DRAW); #else g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_WRITE_COLOR); #endif - if (g_dummy_vbo == NULL) { + if (g_dummy_vbo == nullptr) { /* initialize vertex format */ GPUVertFormat format = {0}; uint dummy_id = GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); @@ -141,7 +141,7 @@ static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache #else DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); - ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__); + ParticleRefineCall *pr_call = (ParticleRefineCall *)MEM_mallocN(sizeof(*pr_call), __func__); pr_call->next = g_tf_calls; pr_call->vbo = cache->final[subdiv].proc_buf; pr_call->shgrp = tf_shgrp; @@ -153,7 +153,7 @@ static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache #endif drw_hair_particle_cache_shgrp_attach_resources(tf_shgrp, cache, subdiv); - DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len); + DRW_shgroup_call_procedural_points(tf_shgrp, nullptr, final_points_len); } } @@ -188,7 +188,7 @@ GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, Modifi int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; ParticleHairCache *cache = drw_hair_particle_cache_get( - object, psys, md, NULL, subdiv, thickness_res); + object, psys, md, nullptr, subdiv, thickness_res); return cache->final[subdiv].proc_buf; } @@ -201,11 +201,11 @@ void DRW_hair_duplimat_get(Object *object, Object *dupli_parent = DRW_object_get_dupli_parent(object); DupliObject *dupli_object = DRW_object_get_dupli(object); - if ((dupli_parent != NULL) && (dupli_object != NULL)) { + if ((dupli_parent != nullptr) && (dupli_object != nullptr)) { if (dupli_object->type & OB_DUPLICOLLECTION) { unit_m4(dupli_mat); Collection *collection = dupli_parent->instance_collection; - if (collection != NULL) { + if (collection != nullptr) { sub_v3_v3(dupli_mat[3], collection->instance_offset); } mul_m4_m4m4(dupli_mat, dupli_parent->obmat, dupli_mat); @@ -291,7 +291,7 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, return shgrp; } -void DRW_hair_update(void) +void DRW_hair_update() { #ifndef USE_TRANSFORM_FEEDBACK /** @@ -304,7 +304,7 @@ void DRW_hair_update(void) * and the most local workaround that still uses the power of the GPU. */ - if (g_tf_calls == NULL) { + if (g_tf_calls == nullptr) { return; } @@ -319,21 +319,22 @@ void DRW_hair_update(void) * Do chunks of maximum 2048 * 2048 hair points. */ int width = 2048; int height = min_ii(width, 1 + max_size / width); - GPUTexture *tex = DRW_texture_pool_query_2d(width, height, GPU_RGBA32F, (void *)DRW_hair_update); + GPUTexture *tex = DRW_texture_pool_query_2d( + width, height, GPU_RGBA32F, (DrawEngineType *)DRW_hair_update); g_tf_target_height = height; g_tf_target_width = width; - GPUFrameBuffer *fb = NULL; + GPUFrameBuffer *fb = nullptr; GPU_framebuffer_ensure_config(&fb, { GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(tex), }); - float *data = MEM_mallocN(sizeof(float[4]) * width * height, "tf fallback buffer"); + float *data = (float *)MEM_mallocN(sizeof(float[4]) * width * height, "tf fallback buffer"); GPU_framebuffer_bind(fb); - while (g_tf_calls != NULL) { + while (g_tf_calls != nullptr) { ParticleRefineCall *pr_call = g_tf_calls; g_tf_calls = g_tf_calls->next; @@ -342,7 +343,7 @@ void DRW_hair_update(void) int max_read_px_len = min_ii(width * height, pr_call->vert_len); DRW_draw_pass_subset(g_tf_pass, pr_call->shgrp, pr_call->shgrp); - /* Readback result to main memory. */ + /* Read back result to main memory. */ GPU_framebuffer_read_color(fb, 0, 0, width, height, 4, 0, GPU_DATA_FLOAT, data); /* Upload back to VBO. */ GPU_vertbuf_use(pr_call->vbo); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 4bbcf6eaf42..b3c4e21639d 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -501,7 +501,7 @@ static DRWData *drw_viewport_data_ensure(GPUViewport *viewport) * - size can be NULL to get it from viewport. * - if viewport and size are NULL, size is set to (1, 1). * - * Important: drw_manager_init can be called multiple times before drw_manager_exit. + * IMPORTANT: #drw_manager_init can be called multiple times before #drw_manager_exit. */ static void drw_manager_init(DRWManager *dst, GPUViewport *viewport, const int size[2]) { @@ -529,7 +529,7 @@ static void drw_manager_init(DRWManager *dst, GPUViewport *viewport, const int s dst->view_data_active = dst->vmempool->view_data[view]; dst->resource_handle = 0; dst->pass_handle = 0; - dst->primary_view_ct = 0; + dst->primary_view_num = 0; drw_viewport_data_reset(dst->vmempool); diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 7a9585262ff..aa0c472be04 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -319,6 +319,8 @@ typedef enum { DRW_UNIFORM_STORAGE_BLOCK, DRW_UNIFORM_STORAGE_BLOCK_REF, DRW_UNIFORM_TFEEDBACK_TARGET, + DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE, + DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE_REF, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF, /** Per drawcall uniforms/UBO */ @@ -617,7 +619,7 @@ typedef struct DRWManager { DRWView *view_default; DRWView *view_active; DRWView *view_previous; - uint primary_view_ct; + uint primary_view_num; /** TODO(@fclem): Remove this. Only here to support * shaders without common_view_lib.glsl */ ViewInfos view_storage_cpy; diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index b5432da0957..19c22f17b32 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -547,6 +547,29 @@ void DRW_shgroup_vertex_buffer_ref_ex(DRWShadingGroup *shgroup, shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF, vertex_buffer, 0, 0, 1); } +void DRW_shgroup_buffer_texture_ex(DRWShadingGroup *shgroup, + const char *name, + GPUVertBuf *vertex_buffer) +{ + int location = GPU_shader_get_ssbo(shgroup->shader, name); + if (location == -1) { + return; + } + drw_shgroup_uniform_create_ex( + shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE, vertex_buffer, 0, 0, 1); +} + +void DRW_shgroup_buffer_texture_ref_ex(DRWShadingGroup *shgroup, + const char *name, + GPUVertBuf **vertex_buffer) +{ + int location = GPU_shader_get_ssbo(shgroup->shader, name); + if (location == -1) { + return; + } + drw_shgroup_uniform_create_ex( + shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE_REF, vertex_buffer, 0, 0, 1); +} /** \} */ /* -------------------------------------------------------------------- */ @@ -937,25 +960,25 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup, } void DRW_shgroup_call_range( - DRWShadingGroup *shgroup, struct Object *ob, GPUBatch *geom, uint v_sta, uint v_ct) + DRWShadingGroup *shgroup, struct Object *ob, GPUBatch *geom, uint v_sta, uint v_num) { BLI_assert(geom != NULL); if (G.f & G_FLAG_PICKSEL) { drw_command_set_select_id(shgroup, NULL, DST.select_id); } DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob); - drw_command_draw_range(shgroup, geom, handle, v_sta, v_ct); + drw_command_draw_range(shgroup, geom, handle, v_sta, v_num); } void DRW_shgroup_call_instance_range( - DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct) + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_num) { BLI_assert(geom != NULL); if (G.f & G_FLAG_PICKSEL) { drw_command_set_select_id(shgroup, NULL, DST.select_id); } DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob); - drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_ct); + drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_num); } void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, @@ -1905,8 +1928,8 @@ DRWView *DRW_view_create(const float viewmat[4][4], { DRWView *view = BLI_memblock_alloc(DST.vmempool->views); - if (DST.primary_view_ct < MAX_CULLED_VIEWS) { - view->culling_mask = 1u << DST.primary_view_ct++; + if (DST.primary_view_num < MAX_CULLED_VIEWS) { + view->culling_mask = 1u << DST.primary_view_num++; } else { BLI_assert(0); @@ -2058,6 +2081,11 @@ void DRW_view_camtexco_set(DRWView *view, float texco[4]) copy_v4_v4(view->storage.viewcamtexcofac, texco); } +void DRW_view_camtexco_get(const DRWView *view, float r_texco[4]) +{ + copy_v4_v4(r_texco, view->storage.viewcamtexcofac); +} + void DRW_view_frustum_corners_get(const DRWView *view, BoundBox *corners) { memcpy(corners, &view->frustum_corners, sizeof(view->frustum_corners)); diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 2c5b02f88a9..e7e0e0ce41f 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -693,6 +693,12 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup, *use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader, ((GPUVertBuf *)uni->pvalue)); break; + case DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE_REF: + GPU_vertbuf_bind_as_texture(*uni->vertbuf_ref, uni->location); + break; + case DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE: + GPU_vertbuf_bind_as_texture(uni->vertbuf, uni->location); + break; case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF: GPU_vertbuf_bind_as_ssbo(*uni->vertbuf_ref, uni->location); break; diff --git a/source/blender/draw/intern/draw_shader.c b/source/blender/draw/intern/draw_shader.cc index 063aec24b94..001ceb0ae8d 100644 --- a/source/blender/draw/intern/draw_shader.c +++ b/source/blender/draw/intern/draw_shader.cc @@ -16,15 +16,15 @@ #include "draw_shader.h" -extern char datatoc_common_hair_lib_glsl[]; +extern "C" char datatoc_common_hair_lib_glsl[]; -extern char datatoc_common_hair_refine_vert_glsl[]; -extern char datatoc_common_hair_refine_comp_glsl[]; -extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; +extern "C" char datatoc_common_hair_refine_vert_glsl[]; +extern "C" char datatoc_common_hair_refine_comp_glsl[]; +extern "C" char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; static struct { struct GPUShader *hair_refine_sh[PART_REFINE_MAX_SHADER]; -} e_data = {{NULL}}; +} e_data = {{nullptr}}; /* -------------------------------------------------------------------- */ /** \name Hair refinement @@ -38,19 +38,19 @@ static GPUShader *hair_refine_shader_compute_create(ParticleRefineShader UNUSED( static GPUShader *hair_refine_shader_transform_feedback_create( ParticleRefineShader UNUSED(refinement)) { - GPUShader *sh = NULL; + GPUShader *sh = nullptr; + + std::string shader_src = std::string(datatoc_common_hair_lib_glsl) + + std::string(datatoc_common_hair_refine_vert_glsl); - char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl, - datatoc_common_hair_refine_vert_glsl); const char *var_names[1] = {"finalColor"}; - sh = DRW_shader_create_with_transform_feedback(shader_src, - NULL, + sh = DRW_shader_create_with_transform_feedback(shader_src.c_str(), + nullptr, "#define HAIR_PHASE_SUBDIV\n" "#define USE_TF\n", GPU_SHADER_TFB_POINTS, var_names, 1); - MEM_freeN(shader_src); return sh; } @@ -64,8 +64,8 @@ static GPUShader *hair_refine_shader_transform_feedback_workaround_create( GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, eParticleRefineShaderType sh_type) { - if (e_data.hair_refine_sh[refinement] == NULL) { - GPUShader *sh = NULL; + if (e_data.hair_refine_sh[refinement] == nullptr) { + GPUShader *sh = nullptr; switch (sh_type) { case PART_REFINE_SHADER_COMPUTE: sh = hair_refine_shader_compute_create(refinement); @@ -88,8 +88,8 @@ GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, eParticleRefineShaderType sh_type) { /* TODO: Implement curves evaluation types (Bezier and Catmull Rom). */ - if (e_data.hair_refine_sh[type] == NULL) { - GPUShader *sh = NULL; + if (e_data.hair_refine_sh[type] == nullptr) { + GPUShader *sh = nullptr; switch (sh_type) { case PART_REFINE_SHADER_COMPUTE: sh = hair_refine_shader_compute_create(PART_REFINE_CATMULL_ROM); @@ -111,7 +111,7 @@ GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, eParticleRefineSh /** \} */ -void DRW_shaders_free(void) +void DRW_shaders_free() { for (int i = 0; i < PART_REFINE_MAX_SHADER; i++) { DRW_SHADER_FREE_SAFE(e_data.hair_refine_sh[i]); diff --git a/source/blender/draw/intern/draw_shader.h b/source/blender/draw/intern/draw_shader.h index 650e78c9362..63d755cc334 100644 --- a/source/blender/draw/intern/draw_shader.h +++ b/source/blender/draw/intern/draw_shader.h @@ -22,7 +22,7 @@ typedef enum eParticleRefineShaderType { PART_REFINE_SHADER_COMPUTE, } eParticleRefineShaderType; -/* draw_shader.c */ +/* draw_shader.cc */ struct GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, eParticleRefineShaderType sh_type); diff --git a/source/blender/draw/intern/draw_subdivision.h b/source/blender/draw/intern/draw_subdivision.h index b7cd520f54f..2d24d07e037 100644 --- a/source/blender/draw/intern/draw_subdivision.h +++ b/source/blender/draw/intern/draw_subdivision.h @@ -160,8 +160,8 @@ typedef struct DRWSubdivCache { /* Contains the start loop index and the smooth flag for each coarse polygon. */ struct GPUVertBuf *extra_coarse_face_data; - /* Computed for ibo.points, one value per subdivided vertex, mapping coarse vertices -> - * subdivided loop */ + /* Computed for `ibo.points`, one value per subdivided vertex, + * mapping coarse vertices -> subdivided loop. */ int *point_indices; /* Material offsets. */ diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.h b/source/blender/draw/intern/mesh_extractors/extract_mesh.h index d55386dfd7d..b88cd9e77d2 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.h +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.h @@ -67,7 +67,7 @@ typedef struct MeshRenderData { const float (*bm_poly_normals)[3]; const float (*bm_poly_centers)[3]; - int *v_origindex, *e_origindex, *p_origindex; + const int *v_origindex, *e_origindex, *p_origindex; int edge_crease_ofs; int vert_crease_ofs; int bweight_ofs; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc index f9e58709c6e..d275b672366 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -180,7 +180,7 @@ static void fill_vertbuf_with_attribute(const MeshRenderData *mr, const MPoly *mpoly = mr->mpoly; const MLoop *mloop = mr->mloop; - const AttributeType *attr_data = static_cast<AttributeType *>( + const AttributeType *attr_data = static_cast<const AttributeType *>( CustomData_get_layer_n(custom_data, request.cd_type, layer_index)); using converter = attribute_type_converter<AttributeType, VBOType>; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc index 4ced14ab11a..14f61dcd739 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc @@ -26,7 +26,7 @@ struct UVStretchAngle { struct MeshExtract_StretchAngle_Data { UVStretchAngle *vbo_data; - MLoopUV *luv; + const MLoopUV *luv; float auv[2][2], last_auv[2]; float av[2][3], last_av[3]; int cd_ofs; @@ -98,7 +98,7 @@ static void extract_edituv_stretch_angle_init(const MeshRenderData *mr, } else { BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH)); - data->luv = (MLoopUV *)CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); + data->luv = (const MLoopUV *)CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); } } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc index 26f0b07f676..e7a3cb03903 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc @@ -17,7 +17,7 @@ namespace blender::draw { struct MeshExtract_FdotUV_Data { float (*vbo_data)[2]; - MLoopUV *uv_data; + const MLoopUV *uv_data; int cd_ofs; }; @@ -49,7 +49,7 @@ static void extract_fdots_uv_init(const MeshRenderData *mr, data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); } else { - data->uv_data = (MLoopUV *)CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); + data->uv_data = (const MLoopUV *)CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); } } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc index ed1a0ccd178..b45a73a27c0 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc @@ -17,7 +17,7 @@ namespace blender::draw { struct MeshExtract_Orco_Data { float (*vbo_data)[4]; - float (*orco)[3]; + const float (*orco)[3]; }; static void extract_orco_init(const MeshRenderData *mr, @@ -42,7 +42,7 @@ static void extract_orco_init(const MeshRenderData *mr, MeshExtract_Orco_Data *data = static_cast<MeshExtract_Orco_Data *>(tls_data); data->vbo_data = (float(*)[4])GPU_vertbuf_get_data(vbo); - data->orco = static_cast<float(*)[3]>(CustomData_get_layer(cd_vdata, CD_ORCO)); + data->orco = static_cast<const float(*)[3]>(CustomData_get_layer(cd_vdata, CD_ORCO)); /* Make sure `orco` layer was requested only if needed! */ BLI_assert(data->orco); } @@ -105,7 +105,7 @@ static void extract_orco_init_subdiv(const DRWSubdivCache *subdiv_cache, float(*coarse_vbo_data)[4] = static_cast<float(*)[4]>(GPU_vertbuf_get_data(coarse_vbo)); CustomData *cd_vdata = &mr->me->vdata; - float(*orco)[3] = static_cast<float(*)[3]>(CustomData_get_layer(cd_vdata, CD_ORCO)); + const float(*orco)[3] = static_cast<const float(*)[3]>(CustomData_get_layer(cd_vdata, CD_ORCO)); if (mr->extract_type == MR_EXTRACT_MESH) { const MLoop *mloop = mr->mloop; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc index ea46d9c4caa..c8c91d45542 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc @@ -220,7 +220,7 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache, if (subdiv_cache->use_custom_loop_normals) { Mesh *coarse_mesh = subdiv_cache->mesh; - float(*lnors)[3] = static_cast<float(*)[3]>( + const float(*lnors)[3] = static_cast<float(*)[3]>( CustomData_get_layer(&coarse_mesh->ldata, CD_NORMAL)); BLI_assert(lnors != nullptr); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc index 96595df9276..5658ed85c8b 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc @@ -42,8 +42,8 @@ static void extract_sculpt_data_init(const MeshRenderData *mr, CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; CustomData *cd_pdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata; - float *cd_mask = (float *)CustomData_get_layer(cd_vdata, CD_PAINT_MASK); - int *cd_face_set = (int *)CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS); + const float *cd_mask = (const float *)CustomData_get_layer(cd_vdata, CD_PAINT_MASK); + const int *cd_face_set = (const int *)CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS); GPU_vertbuf_init_with_format(vbo, format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -54,7 +54,7 @@ static void extract_sculpt_data_init(const MeshRenderData *mr, }; gpuSculptData *vbo_data = (gpuSculptData *)GPU_vertbuf_get_data(vbo); - MLoop *loops = (MLoop *)CustomData_get_layer(cd_ldata, CD_MLOOP); + const MLoop *loops = (const MLoop *)CustomData_get_layer(cd_ldata, CD_MLOOP); if (mr->extract_type == MR_EXTRACT_BMESH) { int cd_mask_ofs = CustomData_get_offset(cd_vdata, CD_PAINT_MASK); @@ -126,7 +126,7 @@ static void extract_sculpt_data_init_subdiv(const DRWSubdivCache *subdiv_cache, /* First, interpolate mask if available. */ GPUVertBuf *mask_vbo = nullptr; GPUVertBuf *subdiv_mask_vbo = nullptr; - float *cd_mask = (float *)CustomData_get_layer(cd_vdata, CD_PAINT_MASK); + const float *cd_mask = (const float *)CustomData_get_layer(cd_vdata, CD_PAINT_MASK); if (cd_mask) { GPUVertFormat mask_format = {0}; @@ -167,7 +167,7 @@ static void extract_sculpt_data_init_subdiv(const DRWSubdivCache *subdiv_cache, }; gpuFaceSet *face_sets = (gpuFaceSet *)GPU_vertbuf_get_data(face_set_vbo); - int *cd_face_set = (int *)CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS); + const int *cd_face_set = (const int *)CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS); GPUVertFormat *format = get_sculpt_data_format(); GPU_vertbuf_init_build_on_device(vbo, format, subdiv_cache->num_subdiv_loops); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc index 2a4a6a186be..f4c54b2f881 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc @@ -188,7 +188,7 @@ static void extract_vert_idx_init_subdiv(const DRWSubdivCache *subdiv_cache, { GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf); const DRWSubdivLooseGeom &loose_geom = subdiv_cache->loose_geom; - /* Each element points to an element in the ibo.points. */ + /* Each element points to an element in the `ibo.points`. */ draw_subdiv_init_origindex_buffer( vbo, (int32_t *)GPU_vertbuf_get_data(subdiv_cache->verts_orig_index), diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc index 25f78d68914..91cd675d32f 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc @@ -291,7 +291,8 @@ static void extract_tan_init_subdiv(const DRWSubdivCache *subdiv_cache, for (int i = 0; i < tan_len; i++) { float(*tan_data)[4] = (float(*)[4])GPU_vertbuf_get_data(coarse_vbo); const char *name = tangent_names[i]; - float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(&loop_data, CD_TANGENT, name); + const float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named( + &loop_data, CD_TANGENT, name); for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { copy_v3_v3(*tan_data, layer_data[ml_index]); (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? 1.0f : -1.0f; @@ -306,7 +307,7 @@ static void extract_tan_init_subdiv(const DRWSubdivCache *subdiv_cache, } if (use_orco_tan) { float(*tan_data)[4] = (float(*)[4])GPU_vertbuf_get_data(coarse_vbo); - float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0); + const float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0); for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { copy_v3_v3(*tan_data, layer_data[ml_index]); (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? 1.0f : -1.0f; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc index 5fb4b401ae3..2808a0a3a71 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc @@ -108,7 +108,8 @@ static void extract_uv_init(const MeshRenderData *mr, } } else { - MLoopUV *layer_data = (MLoopUV *)CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i); + const MLoopUV *layer_data = (const MLoopUV *)CustomData_get_layer_n( + cd_ldata, CD_MLOOPUV, i); for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, uv_data++, layer_data++) { memcpy(uv_data, layer_data->uv, sizeof(*uv_data)); } diff --git a/source/blender/draw/intern/shaders/common_globals_lib.glsl b/source/blender/draw/intern/shaders/common_globals_lib.glsl index 0460ba56e4c..a8931292064 100644 --- a/source/blender/draw/intern/shaders/common_globals_lib.glsl +++ b/source/blender/draw/intern/shaders/common_globals_lib.glsl @@ -110,7 +110,7 @@ layout(std140) uniform globalsBlock vec4 screenVecs[2]; vec4 sizeViewport; /* Inverted size in zw. */ - float sizePixel; /* This one is for dpi scaling */ + float sizePixel; /* This one is for DPI scaling. */ float pixelFac; /* To use with mul_project_m4_v3_zfac() */ float sizeObjectCenter; float sizeLightCenter; diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl index ff52b483d77..dfa2f307800 100644 --- a/source/blender/draw/intern/shaders/common_hair_lib.glsl +++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl @@ -212,8 +212,8 @@ void hair_get_pos_tan_binor_time(bool is_persp, wpos += wbinor * thick_time * scale; } else { - /* Note: Ensures 'hairThickTime' is initialised - - * avoids undefined behaviour on certain macOS configurations. */ + /* NOTE: Ensures 'hairThickTime' is initialized - + * avoids undefined behavior on certain macOS configurations. */ thick_time = 0.0; } } diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 7bb1d652bf1..c2d517588b2 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -4020,6 +4020,8 @@ static bool acf_nlaaction_setting_valid(bAnimContext *UNUSED(ac), else { return false; } + case ACHANNEL_SETTING_SELECT: /* selected */ + return true; /* unsupported */ default: @@ -4040,6 +4042,9 @@ static int acf_nlaaction_setting_flag(bAnimContext *UNUSED(ac), *neg = true; /* XXX */ return ADT_NLA_EDIT_NOMAP; + case ACHANNEL_SETTING_SELECT: /* selected */ + return ADT_UI_SELECTED; + default: /* unsupported */ return 0; } diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 31d90c8bfec..f148bb5b77d 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -125,6 +125,7 @@ void ANIM_set_active_channel(bAnimContext *ac, case ANIMTYPE_DSHAIR: case ANIMTYPE_DSPOINTCLOUD: case ANIMTYPE_DSVOLUME: + case ANIMTYPE_NLAACTION: case ANIMTYPE_DSSIMULATION: { /* need to verify that this data is valid for now */ if (ale->adt) { @@ -182,6 +183,7 @@ void ANIM_set_active_channel(bAnimContext *ac, case ANIMTYPE_DSHAIR: case ANIMTYPE_DSPOINTCLOUD: case ANIMTYPE_DSVOLUME: + case ANIMTYPE_NLAACTION: case ANIMTYPE_DSSIMULATION: { /* need to verify that this data is valid for now */ if (ale && ale->adt) { @@ -199,7 +201,6 @@ void ANIM_set_active_channel(bAnimContext *ac, /* unhandled currently, but may be interesting */ case ANIMTYPE_MASKLAYER: case ANIMTYPE_SHAPEKEY: - case ANIMTYPE_NLAACTION: break; /* other types */ @@ -312,6 +313,7 @@ static eAnimChannels_SetFlag anim_channels_selection_flag_for_toggle(const ListB case ANIMTYPE_DSHAIR: case ANIMTYPE_DSPOINTCLOUD: case ANIMTYPE_DSVOLUME: + case ANIMTYPE_NLAACTION: case ANIMTYPE_DSSIMULATION: { if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED)) { return ACHANNEL_SETFLAG_CLEAR; @@ -420,6 +422,7 @@ static void anim_channels_select_set(bAnimContext *ac, case ANIMTYPE_DSHAIR: case ANIMTYPE_DSPOINTCLOUD: case ANIMTYPE_DSVOLUME: + case ANIMTYPE_NLAACTION: case ANIMTYPE_DSSIMULATION: { /* need to verify that this data is valid for now */ if (ale->adt) { diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 58d093c678d..786204a52ed 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -168,11 +168,11 @@ void draw_keyframe_shape(float x, /* Common attributes shared between the draw calls. */ typedef struct DrawKeylistUIData { float alpha; - float icon_sz; - float half_icon_sz; - float smaller_sz; - float ipo_sz; - float gpencil_sz; + float icon_size; + float half_icon_size; + float smaller_size; + float ipo_size; + float gpencil_size; float screenspace_margin; float sel_color[4]; float unsel_color[4]; @@ -195,11 +195,11 @@ static void draw_keylist_ui_data_init(DrawKeylistUIData *ctx, /* TODO: allow this opacity factor to be themed? */ ctx->alpha = channel_locked ? 0.25f : 1.0f; - ctx->icon_sz = U.widget_unit * 0.5f * yscale_fac; - ctx->half_icon_sz = 0.5f * ctx->icon_sz; - ctx->smaller_sz = 0.35f * ctx->icon_sz; - ctx->ipo_sz = 0.1f * ctx->icon_sz; - ctx->gpencil_sz = ctx->smaller_sz * 0.8f; + ctx->icon_size = U.widget_unit * 0.5f * yscale_fac; + ctx->half_icon_size = 0.5f * ctx->icon_size; + ctx->smaller_size = 0.35f * ctx->icon_size; + ctx->ipo_size = 0.1f * ctx->icon_size; + ctx->gpencil_size = ctx->smaller_size * 0.8f; ctx->screenspace_margin = (0.35f * (float)UI_UNIT_X) / UI_view2d_scale_get_x(v2d); ctx->show_ipo = (saction_flag & SACTION_SHOW_INTERPOLATION) != 0; @@ -242,8 +242,8 @@ static void draw_keylist_block_gpencil(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = min_ff(ab->next->cfra - (ctx->screenspace_margin * size), ab->next->cfra), - .ymin = ypos - ctx->gpencil_sz, - .ymax = ypos + ctx->gpencil_sz, + .ymin = ypos - ctx->gpencil_size, + .ymax = ypos + ctx->gpencil_size, }, true, 0.25f * (float)UI_UNIT_X, @@ -259,8 +259,8 @@ static void draw_keylist_block_moving_hold(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = ab->next->cfra, - .ymin = ypos - ctx->smaller_sz, - .ymax = ypos + ctx->smaller_sz, + .ymin = ypos - ctx->smaller_size, + .ymax = ypos + ctx->smaller_size, }, true, 3.0f, @@ -275,8 +275,8 @@ static void draw_keylist_block_standard(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = ab->next->cfra, - .ymin = ypos - ctx->half_icon_sz, - .ymax = ypos + ctx->half_icon_sz, + .ymin = ypos - ctx->half_icon_size, + .ymax = ypos + ctx->half_icon_size, }, true, 3.0f, @@ -291,8 +291,8 @@ static void draw_keylist_block_interpolation_line(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = ab->next->cfra, - .ymin = ypos - ctx->ipo_sz, - .ymax = ypos + ctx->ipo_sz, + .ymin = ypos - ctx->ipo_size, + .ymax = ypos + ctx->ipo_size, }, true, 3.0f, @@ -367,7 +367,7 @@ static void draw_keylist_keys(const DrawKeylistUIData *ctx, draw_keyframe_shape(ak->cfra, ypos, - ctx->icon_sz, + ctx->icon_size, (ak->sel & SELECT), ak->key_type, KEYFRAME_SHAPE_BOTH, diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c index 64035732a39..3a1e7419d3c 100644 --- a/source/blender/editors/armature/armature_skinning.c +++ b/source/blender/editors/armature/armature_skinning.c @@ -9,6 +9,7 @@ #include "DNA_armature_types.h" #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" diff --git a/source/blender/editors/curve/CMakeLists.txt b/source/blender/editors/curve/CMakeLists.txt index b21ae305dbe..791e28de694 100644 --- a/source/blender/editors/curve/CMakeLists.txt +++ b/source/blender/editors/curve/CMakeLists.txt @@ -23,9 +23,9 @@ set(SRC editcurve.c editcurve_add.c editcurve_paint.c + editcurve_pen.c editcurve_query.c editcurve_select.c - editcurve_pen.c editcurve_undo.c editfont.c editfont_undo.c diff --git a/source/blender/editors/curves/CMakeLists.txt b/source/blender/editors/curves/CMakeLists.txt index 1731d224b3e..383640504c4 100644 --- a/source/blender/editors/curves/CMakeLists.txt +++ b/source/blender/editors/curves/CMakeLists.txt @@ -11,6 +11,9 @@ set(INC ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + + # RNA_prototypes.h + ${CMAKE_BINARY_DIR}/source/blender/makesrna ) set(SRC @@ -24,3 +27,5 @@ set(LIB ) blender_add_lib(bf_editor_curves "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") +add_dependencies(bf_editor_curves bf_rna) + diff --git a/source/blender/editors/curves/intern/curves_add.cc b/source/blender/editors/curves/intern/curves_add.cc index 7d9e663c444..552ef1d96c8 100644 --- a/source/blender/editors/curves/intern/curves_add.cc +++ b/source/blender/editors/curves/intern/curves_add.cc @@ -20,7 +20,7 @@ bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int poi MutableSpan<float3> positions = curves.positions_for_write(); float *radius_data = (float *)CustomData_add_layer_named( - &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_size, "radius"); + &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_num, "radius"); MutableSpan<float> radii{radius_data, curves.points_num()}; for (const int i : offsets.index_range()) { diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index 7d07c211542..5588f7440a8 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -20,6 +20,7 @@ #include "BKE_layer.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" +#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" #include "BKE_report.h" @@ -32,9 +33,11 @@ #include "DNA_scene_types.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_prototypes.h" /** * The code below uses a suffix naming convention to indicate the coordinate space: @@ -192,7 +195,7 @@ static void try_convert_single_object(Object &curves_ob, /* Prepare utility data structure to map hair roots to mfaces. */ const Span<int> mface_to_poly_map{ - static_cast<int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)), + static_cast<const int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)), surface_me.totface}; Array<Vector<int>> poly_to_mface_map(surface_me.totpoly); for (const int mface_i : mface_to_poly_map.index_range()) { @@ -315,6 +318,140 @@ static void CURVES_OT_convert_to_particle_system(wmOperatorType *ot) ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; } +namespace convert_from_particle_system { + +static bke::CurvesGeometry particles_to_curves(Object &object, ParticleSystem &psys) +{ + ParticleSettings &settings = *psys.part; + if (psys.part->type != PART_HAIR) { + return {}; + } + + const bool transfer_parents = (settings.draw & PART_DRAW_PARENT) || settings.childtype == 0; + + const Span<ParticleCacheKey *> parents_cache{psys.pathcache, psys.totcached}; + const Span<ParticleCacheKey *> children_cache{psys.childcache, psys.totchildcache}; + + int points_num = 0; + Vector<int> curve_offsets; + Vector<int> parents_to_transfer; + Vector<int> children_to_transfer; + if (transfer_parents) { + for (const int parent_i : parents_cache.index_range()) { + const int segments = parents_cache[parent_i]->segments; + if (segments <= 0) { + continue; + } + parents_to_transfer.append(parent_i); + curve_offsets.append(points_num); + points_num += segments + 1; + } + } + for (const int child_i : children_cache.index_range()) { + const int segments = children_cache[child_i]->segments; + if (segments <= 0) { + continue; + } + children_to_transfer.append(child_i); + curve_offsets.append(points_num); + points_num += segments + 1; + } + const int curves_num = parents_to_transfer.size() + children_to_transfer.size(); + curve_offsets.append(points_num); + BLI_assert(curve_offsets.size() == curves_num + 1); + bke::CurvesGeometry curves(points_num, curves_num); + curves.offsets_for_write().copy_from(curve_offsets); + + const float4x4 object_to_world_mat = object.obmat; + const float4x4 world_to_object_mat = object_to_world_mat.inverted(); + + MutableSpan<float3> positions = curves.positions_for_write(); + + const auto copy_hair_to_curves = [&](const Span<ParticleCacheKey *> hair_cache, + const Span<int> indices_to_transfer, + const int curve_index_offset) { + threading::parallel_for(indices_to_transfer.index_range(), 256, [&](const IndexRange range) { + for (const int i : range) { + const int hair_i = indices_to_transfer[i]; + const int curve_i = i + curve_index_offset; + const IndexRange points = curves.points_for_curve(curve_i); + const Span<ParticleCacheKey> keys{hair_cache[hair_i], points.size()}; + for (const int key_i : keys.index_range()) { + const float3 key_pos_wo = keys[key_i].co; + positions[points[key_i]] = world_to_object_mat * key_pos_wo; + } + } + }); + }; + + if (transfer_parents) { + copy_hair_to_curves(parents_cache, parents_to_transfer, 0); + } + copy_hair_to_curves(children_cache, children_to_transfer, parents_to_transfer.size()); + + curves.update_curve_types(); + curves.tag_topology_changed(); + return curves; +} + +static int curves_convert_from_particle_system_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main &bmain = *CTX_data_main(C); + ViewLayer &view_layer = *CTX_data_view_layer(C); + Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C); + Object *ob_from_orig = ED_object_active_context(C); + ParticleSystem *psys_orig = static_cast<ParticleSystem *>( + CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data); + if (psys_orig == nullptr) { + psys_orig = psys_get_current(ob_from_orig); + } + if (psys_orig == nullptr) { + return OPERATOR_CANCELLED; + } + Object *ob_from_eval = DEG_get_evaluated_object(&depsgraph, ob_from_orig); + ParticleSystem *psys_eval = nullptr; + LISTBASE_FOREACH (ModifierData *, md, &ob_from_eval->modifiers) { + if (md->type != eModifierType_ParticleSystem) { + continue; + } + ParticleSystemModifierData *psmd = reinterpret_cast<ParticleSystemModifierData *>(md); + if (!STREQ(psmd->psys->name, psys_orig->name)) { + continue; + } + psys_eval = psmd->psys; + } + + Object *ob_new = BKE_object_add(&bmain, &view_layer, OB_CURVES, psys_eval->name); + ob_new->dtx |= OB_DRAWBOUNDOX; /* TODO: Remove once there is actual drawing. */ + Curves *curves_id = static_cast<Curves *>(ob_new->data); + BKE_object_apply_mat4(ob_new, ob_from_orig->obmat, true, false); + bke::CurvesGeometry::wrap(curves_id->geometry) = particles_to_curves(*ob_from_eval, *psys_eval); + + DEG_relations_tag_update(&bmain); + WM_main_add_notifier(NC_OBJECT | ND_DRAW, nullptr); + + return OPERATOR_FINISHED; +} + +static bool curves_convert_from_particle_system_poll(bContext *C) +{ + return ED_object_active_context(C) != nullptr; +} + +} // namespace convert_from_particle_system + +static void CURVES_OT_convert_from_particle_system(wmOperatorType *ot) +{ + ot->name = "Convert Particle System to Curves"; + ot->idname = "CURVES_OT_convert_from_particle_system"; + ot->description = "Add a new curves object based on the current state of the particle system"; + + ot->poll = convert_from_particle_system::curves_convert_from_particle_system_poll; + ot->exec = convert_from_particle_system::curves_convert_from_particle_system_exec; + + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; +} + namespace snap_curves_to_surface { enum class AttachMode { @@ -514,5 +651,6 @@ void ED_operatortypes_curves() { using namespace blender::ed::curves; WM_operatortype_append(CURVES_OT_convert_to_particle_system); + WM_operatortype_append(CURVES_OT_convert_from_particle_system); WM_operatortype_append(CURVES_OT_snap_curves_to_surface); } diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 9e7e00d5656..d8ecb2c5ae0 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -777,6 +777,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.curves.sculpt_cut ops.curves.sculpt_delete ops.curves.sculpt_grow_shrink + ops.curves.sculpt_puff ops.curves.sculpt_snake_hook ops.generic.cursor ops.generic.select diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc index 05f9e19da71..cfc158b117f 100644 --- a/source/blender/editors/geometry/geometry_attributes.cc +++ b/source/blender/editors/geometry/geometry_attributes.cc @@ -385,24 +385,16 @@ void GEOMETRY_OT_color_attribute_add(wmOperatorType *ot) ot->srna, "name", "Color", MAX_NAME, "Name", "Name of new color attribute"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - static EnumPropertyItem domains[3] = {{ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", ""}, - {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""}, - {0, nullptr, 0, nullptr, nullptr}}; - - static EnumPropertyItem types[3] = {{CD_PROP_COLOR, "COLOR", 0, "Color", ""}, - {CD_PROP_BYTE_COLOR, "BYTE_COLOR", 0, "Byte Color", ""}, - {0, nullptr, 0, nullptr, nullptr}}; - prop = RNA_def_enum(ot->srna, "domain", - domains, + rna_enum_color_attribute_domain_items, ATTR_DOMAIN_POINT, "Domain", "Type of element that attribute is stored on"); prop = RNA_def_enum(ot->srna, "data_type", - types, + rna_enum_color_attribute_type_items, CD_PROP_COLOR, "Data Type", "Type of data stored in attribute"); diff --git a/source/blender/editors/gizmo_library/gizmo_draw_utils.c b/source/blender/editors/gizmo_library/gizmo_draw_utils.c index c6303c197e7..178a2b52b62 100644 --- a/source/blender/editors/gizmo_library/gizmo_draw_utils.c +++ b/source/blender/editors/gizmo_library/gizmo_draw_utils.c @@ -95,7 +95,7 @@ void wm_gizmo_vec_draw( immEnd(); } else if (primitive_type == GPU_PRIM_TRI_FAN) { - /* Note(Metal): Tri-fan alternative for Metal. Triangle List is more efficient for small + /* NOTE(Metal): Tri-fan alternative for Metal. Triangle List is more efficient for small * primitive counts. */ int tri_count = vert_count - 2; immBegin(GPU_PRIM_TRIS, tri_count * 3); diff --git a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c index b326d6d1859..a0e30c7518a 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c @@ -74,7 +74,7 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz, GPU_viewport_size_get_f(viewport); GPUVertFormat *format = immVertexFormat(); - /* Note(Metal): Prefer 3D coordinate for 2D rendering when using 3D shader. */ + /* NOTE(Metal): Prefer 3D coordinate for 2D rendering when using 3D shader. */ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); /* TODO: other draw styles. */ diff --git a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c index a76242404ba..1d9fc35eda8 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c @@ -96,7 +96,7 @@ static void dial_geom_draw(const float color[4], ED_GIZMO_DIAL_DRAW_FLAG_FILL))); GPUVertFormat *format = immVertexFormat(); - /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */ + /* NOTE(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); if (clip_plane) { diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c index 5fb1173521a..46fede8f6be 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c @@ -98,7 +98,7 @@ static void move_geom_draw(const wmGizmo *gz, ED_GIZMO_MOVE_DRAW_FLAG_FILL))); GPUVertFormat *format = immVertexFormat(); - /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */ + /* NOTE(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(filled ? GPU_SHADER_3D_UNIFORM_COLOR : diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index 3cdf364e4b2..01ac02a9a1d 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -146,6 +146,10 @@ typedef struct tGP_BrushEditData { float inv_mat[4][4]; RNG *rng; + /* Auto-masking strokes. */ + struct GHash *automasking_strokes; + bool automasking_ready; + } tGP_BrushEditData; /* Callback for performing some brush operation on a single point */ @@ -1182,9 +1186,18 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op) gso->region = CTX_wm_region(C); Paint *paint = &ts->gp_sculptpaint->paint; - gso->brush = paint->brush; + Brush *brush = paint->brush; + gso->brush = brush; BKE_curvemapping_init(gso->brush->curve); + if (brush->gpencil_settings->sculpt_mode_flag & + (GP_SCULPT_FLAGMODE_AUTOMASK_STROKE | GP_SCULPT_FLAGMODE_AUTOMASK_LAYER | + GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL)) { + gso->automasking_strokes = BLI_ghash_ptr_new(__func__); + } + else { + gso->automasking_strokes = NULL; + } /* save mask */ gso->mask = ts->gpencil_selectmode_sculpt; @@ -1285,6 +1298,10 @@ static void gpencil_sculpt_brush_exit(bContext *C, wmOperator *op) BLI_rng_free(gso->rng); } + if (gso->automasking_strokes != NULL) { + BLI_ghash_free(gso->automasking_strokes, NULL, NULL); + } + /* Disable headerprints. */ ED_workspace_status_text(C, NULL); @@ -1570,11 +1587,15 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C, bool redo_geom = false; Object *ob = gso->object; bGPdata *gpd = ob->data; - char tool = gso->brush->gpencil_sculpt_tool; + const char tool = gso->brush->gpencil_sculpt_tool; GP_SpaceConversion *gsc = &gso->gsc; Brush *brush = gso->brush; const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure : gso->brush->size; + const bool is_automasking = (brush->gpencil_settings->sculpt_mode_flag & + (GP_SCULPT_FLAGMODE_AUTOMASK_STROKE | + GP_SCULPT_FLAGMODE_AUTOMASK_LAYER | + GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL)) != 0; /* Calc bound box matrix. */ float bound_mat[4][4]; BKE_gpencil_layer_transform_matrix_get(gso->depsgraph, gso->object, gpl, bound_mat); @@ -1589,6 +1610,13 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C, continue; } + { + bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps; + if ((is_automasking) && (!BLI_ghash_haskey(gso->automasking_strokes, gps_active))) { + continue; + } + } + /* Check if the stroke collide with brush. */ if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) { continue; @@ -1699,6 +1727,132 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C, return changed; } +/* Get list of Auto-Masking strokes. */ +static bool get_automasking_strokes_list(tGP_BrushEditData *gso) +{ + bGPdata *gpd = gso->gpd; + GP_SpaceConversion *gsc = &gso->gsc; + Brush *brush = gso->brush; + Object *ob = gso->object; + Material *mat_active = BKE_gpencil_material(ob, ob->actcol); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const bool is_masking_stroke = (brush->gpencil_settings->sculpt_mode_flag & + GP_SCULPT_FLAGMODE_AUTOMASK_STROKE) != 0; + const bool is_masking_layer = (brush->gpencil_settings->sculpt_mode_flag & + GP_SCULPT_FLAGMODE_AUTOMASK_LAYER) != 0; + const bool is_masking_material = (brush->gpencil_settings->sculpt_mode_flag & + GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL) != 0; + int mval_i[2]; + round_v2i_v2fl(mval_i, gso->mval); + + /* Define a fix number of pixel as cursor radius. */ + const int radius = 10; + bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd); + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* Only editable and visible layers are considered. */ + if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + /* Calculate bound box matrix. */ + float bound_mat[4][4]; + BKE_gpencil_layer_transform_matrix_get(gso->depsgraph, gso->object, gpl, bound_mat); + + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (gps->totpoints == 0) { + continue; + } + /* Check if the color is editable. */ + if (ED_gpencil_stroke_material_editable(gso->object, gpl, gps) == false) { + continue; + } + + /* Layer Auto-Masking. */ + if ((is_masking_layer) && (gpl == gpl_active)) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + continue; + } + /* Material Auto-Masking. */ + if (is_masking_material) { + Material *mat = BKE_object_material_get(ob, gps->mat_nr + 1); + if (mat == mat_active) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + continue; + } + } + + /* If Stroke Auto-Masking is not enabled, nothing else to do. */ + if (!is_masking_stroke) { + continue; + } + + /* Check if the stroke collide with brush. */ + if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) { + continue; + } + + bGPDspoint *pt1, *pt2; + int pc1[2] = {0}; + int pc2[2] = {0}; + bGPDspoint npt; + + if (gps->totpoints == 1) { + gpencil_point_to_parent_space(gps->points, bound_mat, &npt); + gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); + + /* Only check if point is inside. */ + if (len_v2v2_int(mval_i, pc1) <= radius) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + } + } + else { + /* Loop over the points in the stroke, checking for intersections + * - an intersection means that we touched the stroke. + */ + for (int i = 0; (i + 1) < gps->totpoints; i++) { + /* Get points to work with. */ + pt1 = gps->points + i; + pt2 = gps->points + i + 1; + + /* Check first point. */ + gpencil_point_to_parent_space(pt1, bound_mat, &npt); + gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); + if (len_v2v2_int(mval_i, pc1) <= radius) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + i = gps->totpoints; + continue; + } + + /* Check second point. */ + gpencil_point_to_parent_space(pt2, bound_mat, &npt); + gpencil_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); + if (len_v2v2_int(mval_i, pc2) <= radius) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + i = gps->totpoints; + continue; + } + + /* Check segment. */ + if (gpencil_stroke_inside_circle(gso->mval, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + i = gps->totpoints; + continue; + } + } + } + } + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; + } + } + } + + return true; +} + /* Perform two-pass brushes which modify the existing strokes */ static bool gpencil_sculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) { @@ -1840,6 +1994,14 @@ static void gpencil_sculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA * gso->brush_rect.xmax = mouse[0] + radius; gso->brush_rect.ymax = mouse[1] + radius; + /* Get list of Auto-Masking strokes. */ + if ((!gso->automasking_ready) && + (brush->gpencil_settings->sculpt_mode_flag & + (GP_SCULPT_FLAGMODE_AUTOMASK_STROKE | GP_SCULPT_FLAGMODE_AUTOMASK_LAYER | + GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL))) { + gso->automasking_ready = get_automasking_strokes_list(gso); + } + /* Apply brush */ char tool = gso->brush->gpencil_sculpt_tool; if (tool == GPSCULPT_TOOL_CLONE) { diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 60af05baed7..da40eef87fd 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -549,7 +549,7 @@ typedef enum eAnimChannels_SetFlag { /* types of settings for AnimChannels */ typedef enum eAnimChannel_Settings { ACHANNEL_SETTING_SELECT = 0, - /** warning: for drawing UI's, need to check if this is off (maybe inverse this later) */ + /** WARNING: for drawing UI's, need to check if this is off (maybe inverse this later). */ ACHANNEL_SETTING_PROTECT = 1, ACHANNEL_SETTING_MUTE = 2, ACHANNEL_SETTING_EXPAND = 3, diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index c367072e6e7..e9fcd2bd5fe 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -164,8 +164,16 @@ void ED_fileselect_window_params_get(const struct wmWindow *win, int win_size[2], bool *is_maximized); +/** + * Return the File Browser area in which \a file_operator is active. + */ struct ScrArea *ED_fileselect_handler_area_find(const struct wmWindow *win, const struct wmOperator *file_operator); +/** + * Check if there is any area in \a win that acts as a modal File Browser (#SpaceFile.op is set) + * and return it. + */ +struct ScrArea *ED_fileselect_handler_area_find_any_with_op(const struct wmWindow *win); /* TODO: Maybe we should move this to BLI? * On the other hand, it's using defines from space-file area, so not sure... */ diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 59a24ed22b6..d69b2a47552 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -384,7 +384,7 @@ void ED_operatormacros_mesh(void); */ void ED_keymap_mesh(struct wmKeyConfig *keyconf); -/* editface.c */ +/* editface.cc */ /** * Copy the face flags, most importantly selection from the mesh to the final derived mesh, @@ -520,7 +520,7 @@ float ED_vgroup_vert_weight(struct Object *ob, struct bDeformGroup *dg, int vert */ void ED_vgroup_vert_active_mirror(struct Object *ob, int def_nr); -/* mesh_data.c */ +/* mesh_data.cc */ void ED_mesh_verts_add(struct Mesh *mesh, struct ReportList *reports, int count); void ED_mesh_edges_add(struct Mesh *mesh, struct ReportList *reports, int count); @@ -591,7 +591,7 @@ void EDBM_redo_state_restore_and_free(struct BMBackup *backup, bool recalc_looptri) ATTR_NONNULL(1, 2); void EDBM_redo_state_free(struct BMBackup *backup) ATTR_NONNULL(1); -/* *** meshtools.c *** */ +/* *** meshtools.cc *** */ int ED_mesh_join_objects_exec(struct bContext *C, struct wmOperator *op); int ED_mesh_shapes_join_objects_exec(struct bContext *C, struct wmOperator *op); diff --git a/source/blender/editors/include/ED_scene.h b/source/blender/editors/include/ED_scene.h index f1a2e5795ee..f67bdb9c1d7 100644 --- a/source/blender/editors/include/ED_scene.h +++ b/source/blender/editors/include/ED_scene.h @@ -18,6 +18,11 @@ struct Scene *ED_scene_add(struct Main *bmain, struct bContext *C, struct wmWindow *win, enum eSceneCopyMethod method) ATTR_NONNULL(); +/** Special mode for adding a scene assigned to sequencer strip. */ +struct Scene *ED_scene_sequencer_add(struct Main *bmain, + struct bContext *C, + enum eSceneCopyMethod method, + const bool assign_strip); /** * \note Only call outside of area/region loops. * \return true if successful. diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 41123aff79f..f909a13c2cb 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -666,7 +666,7 @@ bool ED_region_panel_category_gutter_calc_rect(const ARegion *region, rcti *r_re bool ED_region_panel_category_gutter_isect_xy(const ARegion *region, const int event_xy[2]); /** - * \note: This may return true for multiple overlapping regions. + * \note This may return true for multiple overlapping regions. * If it matters, check overlapped regions first (#ARegion.overlap). */ bool ED_region_contains_xy(const struct ARegion *region, const int event_xy[2]); diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 06134f1d7b5..80a75da27f8 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -24,6 +24,7 @@ struct Object; struct Scene; struct SpaceImage; struct ToolSettings; +struct View2D; struct ViewLayer; struct bContext; struct bNode; @@ -242,15 +243,12 @@ void uvedit_deselect_flush(const struct Scene *scene, struct BMEditMesh *em); */ void uvedit_select_flush(const struct Scene *scene, struct BMEditMesh *em); -bool ED_uvedit_nearest_uv(const struct Scene *scene, - struct Object *obedit, - const float co[2], - float *dist_sq, - float r_uv[2]); -bool ED_uvedit_nearest_uv_multi(const struct Scene *scene, +bool ED_uvedit_nearest_uv_multi(const struct View2D *v2d, + const struct Scene *scene, struct Object **objects, uint objects_len, - const float co[2], + const int mval[2], + const bool ignore_selected, float *dist_sq, float r_uv[2]); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 1e9b68c0920..744f74bf25f 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -7,6 +7,8 @@ #pragma once +#include "BLI_utildefines.h" + #ifdef __cplusplus extern "C" { #endif @@ -256,6 +258,7 @@ typedef enum { */ V3D_PROJ_TEST_CLIP_CONTENT = (1 << 5), } eV3DProjTest; +ENUM_OPERATORS(eV3DProjTest, V3D_PROJ_TEST_CLIP_CONTENT); #define V3D_PROJ_TEST_CLIP_DEFAULT \ (V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR) @@ -443,14 +446,14 @@ void pose_foreachScreenBone(struct ViewContext *vc, void ED_view3d_project_float_v2_m4(const struct ARegion *region, const float co[3], float r_co[2], - float mat[4][4]); + const float mat[4][4]); /** * \note use #ED_view3d_ob_project_mat_get to get projecting mat */ void ED_view3d_project_float_v3_m4(const struct ARegion *region, const float co[3], float r_co[3], - float mat[4][4]); + const float mat[4][4]); eV3DProjStatus ED_view3d_project_base(const struct ARegion *region, struct Base *base); @@ -700,9 +703,9 @@ void ED_view3d_win_to_vector(const struct ARegion *region, const float mval[2], * \param do_clip_planes: Optionally clip the ray by the view clipping planes. * \return success, false if the segment is totally clipped. */ -bool ED_view3d_win_to_segment_clipped(struct Depsgraph *depsgraph, +bool ED_view3d_win_to_segment_clipped(const struct Depsgraph *depsgraph, const struct ARegion *region, - struct View3D *v3d, + const struct View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_end[3], @@ -733,7 +736,7 @@ void ED_view3d_dist_range_get(const struct View3D *v3d, float r_dist_range[2]); /** * \note copies logic of #ED_view3d_viewplane_get(), keep in sync. */ -bool ED_view3d_clip_range_get(struct Depsgraph *depsgraph, +bool ED_view3d_clip_range_get(const struct Depsgraph *depsgraph, const struct View3D *v3d, const struct RegionView3D *rv3d, float *r_clipsta, @@ -952,6 +955,22 @@ float ED_view3d_select_dist_px(void); void ED_view3d_viewcontext_init(struct bContext *C, struct ViewContext *vc, struct Depsgraph *depsgraph); + +/** + * Re-initialize `vc` with `obact` as if it's active object (with some differences). + * + * This is often used when operating on multiple objects in modes (edit, pose mode etc) + * where the `vc` is passed in as an argument which then references it's object data. + * + * \note members #ViewContext.obedit & #ViewContext.em are only initialized if they're already set, + * by #ED_view3d_viewcontext_init in most cases. + * This is necessary because the active object defines the current object-mode. + * When iterating over objects in object-mode it doesn't make sense to perform + * an edit-mode action on an object that happens to contain edit-mode data. + * In some cases these values are cleared allowing the owner of `vc` to explicitly + * disable edit-mode operation (to force object selection in edit-mode for e.g.). + * So object-mode specific values should remain cleared when initialized with another object. + */ void ED_view3d_viewcontext_init_object(struct ViewContext *vc, struct Object *obact); /** * Use this call when executing an operator, diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index d1a6501408c..ea1095b26ff 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -164,7 +164,7 @@ DEF_ICON(NLA) DEF_ICON(PREFERENCES) DEF_ICON(TIME) DEF_ICON(NODETREE) -DEF_ICON_BLANK(181) +DEF_ICON(GEOMETRY_NODES) DEF_ICON(CONSOLE) DEF_ICON_BLANK(183) DEF_ICON(TRACKER) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 1b61e87b140..cc08525c6da 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -221,7 +221,7 @@ enum { UI_BUT_HAS_SEP_CHAR = 1 << 27, /** Don't run updates while dragging (needed in rare cases). */ UI_BUT_UPDATE_DELAY = 1 << 28, - /** When widget is in textedit mode, update value on each char stroke */ + /** When widget is in text-edit mode, update value on each char stroke. */ UI_BUT_TEXTEDIT_UPDATE = 1 << 29, /** Show 'x' icon to clear/unlink value of text or search button. */ UI_BUT_VALUE_CLEAR = 1 << 30, @@ -1539,31 +1539,6 @@ uiBut *uiDefIconTextBlockBut(uiBlock *block, short height, const char *tip); -uiBut *uiDefKeyevtButS(uiBlock *block, - int retval, - const char *str, - int x, - int y, - short width, - short height, - short *spoin, - const char *tip); - -/** - * Short pointers hard-coded. - * \param modkeypoin: will be set to #KM_SHIFT, #KM_ALT, #KM_CTRL, #KM_OSKEY bits. - */ -uiBut *uiDefHotKeyevtButS(uiBlock *block, - int retval, - const char *str, - int x, - int y, - short width, - short height, - short *keypoin, - const short *modkeypoin, - const char *tip); - /** * \param arg: A pointer to string/name, use #UI_but_func_search_set() below to make this work. * here `a1` and `a2`, if set, control thumbnail preview rows/cols. diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h index 6416421f4f5..a1a98a4b08c 100644 --- a/source/blender/editors/include/UI_interface_icons.h +++ b/source/blender/editors/include/UI_interface_icons.h @@ -92,7 +92,7 @@ void UI_icon_render_id_ex(const struct bContext *C, int UI_icon_preview_to_render_size(enum eIconSizes size); /** - * Draws icon with dpi scale factor. + * Draws icon with DPI scale factor. */ void UI_icon_draw(float x, float y, int icon_id); void UI_icon_draw_alpha(float x, float y, int icon_id, float alpha); diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 480044118f1..d6719f0aa9f 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -3849,21 +3849,22 @@ static void ui_but_update_ex(uiBut *but, const bool validate) } case UI_BTYPE_HOTKEY_EVENT: if (but->flag & UI_SELECT) { + const uiButHotkeyEvent *hotkey_but = (uiButHotkeyEvent *)but; - if (but->modifier_key) { + if (hotkey_but->modifier_key) { char *str = but->drawstr; but->drawstr[0] = '\0'; - if (but->modifier_key & KM_SHIFT) { + if (hotkey_but->modifier_key & KM_SHIFT) { str += BLI_strcpy_rlen(str, "Shift "); } - if (but->modifier_key & KM_CTRL) { + if (hotkey_but->modifier_key & KM_CTRL) { str += BLI_strcpy_rlen(str, "Ctrl "); } - if (but->modifier_key & KM_ALT) { + if (hotkey_but->modifier_key & KM_ALT) { str += BLI_strcpy_rlen(str, "Alt "); } - if (but->modifier_key & KM_OSKEY) { + if (hotkey_but->modifier_key & KM_OSKEY) { str += BLI_strcpy_rlen(str, "Cmd "); } @@ -3989,6 +3990,10 @@ static void ui_but_alloc_info(const eButType type, alloc_size = sizeof(uiButTreeRow); alloc_str = "uiButTreeRow"; break; + case UI_BTYPE_HOTKEY_EVENT: + alloc_size = sizeof(uiButHotkeyEvent); + alloc_str = "uiButHotkeyEvent"; + break; default: alloc_size = sizeof(uiBut); alloc_str = "uiBut"; @@ -6280,64 +6285,6 @@ uiBut *uiDefIconBlockBut(uiBlock *block, return but; } -uiBut *uiDefKeyevtButS(uiBlock *block, - int retval, - const char *str, - int x, - int y, - short width, - short height, - short *spoin, - const char *tip) -{ - uiBut *but = ui_def_but(block, - UI_BTYPE_KEY_EVENT | UI_BUT_POIN_SHORT, - retval, - str, - x, - y, - width, - height, - spoin, - 0.0, - 0.0, - 0.0, - 0.0, - tip); - ui_but_update(but); - return but; -} - -uiBut *uiDefHotKeyevtButS(uiBlock *block, - int retval, - const char *str, - int x, - int y, - short width, - short height, - short *keypoin, - const short *modkeypoin, - const char *tip) -{ - uiBut *but = ui_def_but(block, - UI_BTYPE_HOTKEY_EVENT | UI_BUT_POIN_SHORT, - retval, - str, - x, - y, - width, - height, - keypoin, - 0.0, - 0.0, - 0.0, - 0.0, - tip); - but->modifier_key = *modkeypoin; - ui_but_update(but); - return but; -} - uiBut *uiDefSearchBut(uiBlock *block, void *arg, int retval, diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 9d7d76f0bdb..3d347fab89c 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -4507,10 +4507,13 @@ static int ui_do_but_HOTKEYEVT(bContext *C, uiHandleButtonData *data, const wmEvent *event) { + uiButHotkeyEvent *hotkey_but = (uiButHotkeyEvent *)but; + BLI_assert(but->type == UI_BTYPE_HOTKEY_EVENT); + if (data->state == BUTTON_STATE_HIGHLIGHT) { if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) { but->drawstr[0] = 0; - but->modifier_key = 0; + hotkey_but->modifier_key = 0; button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT); return WM_UI_HANDLER_BREAK; } @@ -4544,7 +4547,7 @@ static int ui_do_but_HOTKEYEVT(bContext *C, } /* always set */ - but->modifier_key = event->modifier; + hotkey_but->modifier_key = event->modifier; ui_but_update(but); ED_region_tag_redraw(data->region); diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 1c79d3218fb..2fac7da0ecc 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -229,7 +229,6 @@ struct uiBut { bool changed; /** so buttons can support unit systems which are not RNA */ uchar unit_type; - short modifier_key; short iconadd; /** #UI_BTYPE_BLOCK data */ @@ -381,6 +380,13 @@ typedef struct uiButCurveMapping { eButGradientType gradient_type; } uiButCurveMapping; +/** Derived struct for #UI_BTYPE_HOTKEY_EVENT. */ +typedef struct uiButHotkeyEvent { + uiBut but; + + short modifier_key; +} uiButHotkeyEvent; + /** * Additional, superimposed icon for a button, invoking an operator. */ diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index c1bb2ed6d18..98f3b974d1d 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -961,11 +961,17 @@ static void ui_item_enum_expand_tabs(uiLayout *layout, static void ui_keymap_but_cb(bContext *UNUSED(C), void *but_v, void *UNUSED(key_v)) { uiBut *but = but_v; - - RNA_int_set(&but->rnapoin, "shift", (but->modifier_key & KM_SHIFT) ? KM_MOD_HELD : KM_NOTHING); - RNA_int_set(&but->rnapoin, "ctrl", (but->modifier_key & KM_CTRL) ? KM_MOD_HELD : KM_NOTHING); - RNA_int_set(&but->rnapoin, "alt", (but->modifier_key & KM_ALT) ? KM_MOD_HELD : KM_NOTHING); - RNA_int_set(&but->rnapoin, "oskey", (but->modifier_key & KM_OSKEY) ? KM_MOD_HELD : KM_NOTHING); + BLI_assert(but->type == UI_BTYPE_HOTKEY_EVENT); + const uiButHotkeyEvent *hotkey_but = (uiButHotkeyEvent *)but; + + RNA_int_set( + &but->rnapoin, "shift", (hotkey_but->modifier_key & KM_SHIFT) ? KM_MOD_HELD : KM_NOTHING); + RNA_int_set( + &but->rnapoin, "ctrl", (hotkey_but->modifier_key & KM_CTRL) ? KM_MOD_HELD : KM_NOTHING); + RNA_int_set( + &but->rnapoin, "alt", (hotkey_but->modifier_key & KM_ALT) ? KM_MOD_HELD : KM_NOTHING); + RNA_int_set( + &but->rnapoin, "oskey", (hotkey_but->modifier_key & KM_OSKEY) ? KM_MOD_HELD : KM_NOTHING); } /** diff --git a/source/blender/editors/interface/interface_style.cc b/source/blender/editors/interface/interface_style.cc index 0156a943015..291ede05730 100644 --- a/source/blender/editors/interface/interface_style.cc +++ b/source/blender/editors/interface/interface_style.cc @@ -376,7 +376,7 @@ void uiStyleInit(void) { const uiStyle *style = static_cast<uiStyle *>(U.uistyles.first); - /* recover from uninitialized dpi */ + /* Recover from uninitialized DPI. */ if (U.dpi == 0) { U.dpi = 72; } diff --git a/source/blender/editors/interface/view2d_gizmo_navigate.cc b/source/blender/editors/interface/view2d_gizmo_navigate.cc index 01729e35246..fae28833e4f 100644 --- a/source/blender/editors/interface/view2d_gizmo_navigate.cc +++ b/source/blender/editors/interface/view2d_gizmo_navigate.cc @@ -130,6 +130,13 @@ static bool WIDGETGROUP_navigate_poll(const bContext *C, wmGizmoGroupType *UNUSE } break; } + case SPACE_IMAGE: { + const SpaceImage *sima = static_cast<const SpaceImage *>(area->spacedata.first); + if (sima->gizmo_flag & (SI_GIZMO_HIDE | SI_GIZMO_HIDE_NAVIGATE)) { + return false; + } + break; + } } return true; } diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index fd454083653..87923d9fdf8 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -282,6 +282,7 @@ void WM_OT_alembic_export(wmOperatorType *ot) ot->poll = WM_operator_winactive; ot->ui = wm_alembic_export_draw; ot->check = wm_alembic_export_check; + ot->flag |= OPTYPE_PRESET; WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC, @@ -475,7 +476,7 @@ void WM_OT_alembic_export(wmOperatorType *ot) /* This dummy prop is used to check whether we need to init the start and * end frame values to that of the scene's, otherwise they are reset at * every change, draw update. */ - RNA_def_boolean(ot->srna, "init_scene_frame_range", false, "", ""); + RNA_def_boolean(ot->srna, "init_scene_frame_range", true, "", ""); } /* ************************************************************************** */ diff --git a/source/blender/editors/io/io_cache.c b/source/blender/editors/io/io_cache.c index 624b85358f5..969049313c9 100644 --- a/source/blender/editors/io/io_cache.c +++ b/source/blender/editors/io/io_cache.c @@ -75,17 +75,17 @@ static void open_cancel(bContext *UNUSED(C), wmOperator *op) static int cachefile_open_exec(bContext *C, wmOperator *op) { if (!RNA_struct_property_is_set(op->ptr, "filepath")) { - BKE_report(op->reports, RPT_ERROR, "No filename given"); + BKE_report(op->reports, RPT_ERROR, "No filepath given"); return OPERATOR_CANCELLED; } - char filename[FILE_MAX]; - RNA_string_get(op->ptr, "filepath", filename); + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); Main *bmain = CTX_data_main(C); - CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, BLI_path_basename(filename), 0); - BLI_strncpy(cache_file->filepath, filename, FILE_MAX); + CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, BLI_path_basename(filepath), 0); + BLI_strncpy(cache_file->filepath, filepath, FILE_MAX); DEG_id_tag_update(&cache_file->id, ID_RECALC_COPY_ON_WRITE); /* Will be set when running invoke, not exec directly. */ @@ -182,7 +182,7 @@ static int cachefile_layer_open_invoke(bContext *C, wmOperator *op, const wmEven static int cachefile_layer_add_exec(bContext *C, wmOperator *op) { if (!RNA_struct_property_is_set(op->ptr, "filepath")) { - BKE_report(op->reports, RPT_ERROR, "No filename given"); + BKE_report(op->reports, RPT_ERROR, "No filepath given"); return OPERATOR_CANCELLED; } @@ -192,10 +192,10 @@ static int cachefile_layer_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - char filename[FILE_MAX]; - RNA_string_get(op->ptr, "filepath", filename); + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); - CacheFileLayer *layer = BKE_cachefile_add_layer(cache_file, filename); + CacheFileLayer *layer = BKE_cachefile_add_layer(cache_file, filepath); if (!layer) { WM_report(RPT_ERROR, "Could not add a layer to the cache file"); diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index ca9c2de63a4..609230eefea 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -118,7 +118,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op) const bool generate_preview_surface = RNA_boolean_get(op->ptr, "generate_preview_surface"); const bool export_textures = RNA_boolean_get(op->ptr, "export_textures"); const bool overwrite_textures = RNA_boolean_get(op->ptr, "overwrite_textures"); - const bool relative_texture_paths = RNA_boolean_get(op->ptr, "relative_texture_paths"); + const bool relative_paths = RNA_boolean_get(op->ptr, "relative_paths"); struct USDExportParams params = { export_animation, @@ -133,7 +133,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op) generate_preview_surface, export_textures, overwrite_textures, - relative_texture_paths, + relative_paths, }; bool ok = USD_export(C, filename, ¶ms, as_background_job); @@ -181,9 +181,9 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op) const bool export_tex = RNA_boolean_get(ptr, "export_textures"); uiLayoutSetActive(row, export_mtl && preview && export_tex); - row = uiLayoutRow(col, true); - uiItemR(row, ptr, "relative_texture_paths", 0, NULL, ICON_NONE); - uiLayoutSetActive(row, export_mtl && preview); + box = uiLayoutBox(layout); + col = uiLayoutColumnWithHeading(box, true, IFACE_("File References")); + uiItemR(col, ptr, "relative_paths", 0, NULL, ICON_NONE); box = uiLayoutBox(layout); uiItemL(box, IFACE_("Experimental"), ICON_NONE); @@ -297,10 +297,11 @@ void WM_OT_usd_export(struct wmOperatorType *ot) "Allow overwriting existing texture files when exporting textures"); RNA_def_boolean(ot->srna, - "relative_texture_paths", + "relative_paths", true, - "Relative Texture Paths", - "Make texture asset paths relative to the USD file"); + "Relative Paths", + "Use relative paths to reference external files (i.e. textures, volumes) in " + "USD, otherwise use absolute paths"); } /* ====== USD Import ====== */ diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt index ed09e5a6334..28ac913a3e3 100644 --- a/source/blender/editors/mesh/CMakeLists.txt +++ b/source/blender/editors/mesh/CMakeLists.txt @@ -24,7 +24,7 @@ set(INC ) set(SRC - editface.c + editface.cc editmesh_add.c editmesh_add_gizmo.c editmesh_automerge.c @@ -51,10 +51,10 @@ set(SRC editmesh_tools.c editmesh_undo.c editmesh_utils.c - mesh_data.c + mesh_data.cc mesh_mirror.c mesh_ops.c - meshtools.c + meshtools.cc mesh_intern.h ) diff --git a/source/blender/editors/mesh/editface.c b/source/blender/editors/mesh/editface.cc index a5c6adaa43e..cb5d48d1023 100644 --- a/source/blender/editors/mesh/editface.c +++ b/source/blender/editors/mesh/editface.cc @@ -36,16 +36,16 @@ /* own include */ -void paintface_flush_flags(struct bContext *C, Object *ob, short flag) +void paintface_flush_flags(bContext *C, Object *ob, short flag) { Mesh *me = BKE_mesh_from_object(ob); MPoly *polys, *mp_orig; - const int *index_array = NULL; + const int *index_array = nullptr; int totpoly; BLI_assert((flag & ~(SELECT | ME_HIDE)) == 0); - if (me == NULL) { + if (me == nullptr) { return; } @@ -60,7 +60,7 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag) Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - if (ob_eval == NULL) { + if (ob_eval == nullptr) { return; } @@ -68,7 +68,7 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag) Mesh *me_eval = (Mesh *)ob_eval->runtime.data_eval; bool updated = false; - if (me_orig != NULL && me_eval != NULL && me_orig->totpoly == me->totpoly) { + if (me_orig != nullptr && me_eval != nullptr && me_orig->totpoly == me->totpoly) { /* Update the COW copy of the mesh. */ for (int i = 0; i < me->totpoly; i++) { me_orig->mpoly[i].flag = me->mpoly[i].flag; @@ -79,7 +79,7 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag) updated = true; } /* Mesh polys => Final derived polys */ - else if ((index_array = CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX))) { + else if ((index_array = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX))) { polys = me_eval->mpoly; totpoly = me_eval->totpoly; @@ -104,10 +104,10 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag) BKE_mesh_batch_cache_dirty_tag(me_eval, BKE_MESH_BATCH_DIRTY_SELECT_PAINT); } - DEG_id_tag_update(ob->data, ID_RECALC_SELECT); + DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_SELECT); } else { - DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); + DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); } WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data); @@ -115,18 +115,13 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag) void paintface_hide(bContext *C, Object *ob, const bool unselected) { - Mesh *me; - MPoly *mpoly; - int a; - - me = BKE_mesh_from_object(ob); - if (me == NULL || me->totpoly == 0) { + Mesh *me = BKE_mesh_from_object(ob); + if (me == nullptr || me->totpoly == 0) { return; } - mpoly = me->mpoly; - a = me->totpoly; - while (a--) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mpoly = &me->mpoly[i]; if ((mpoly->flag & ME_HIDE) == 0) { if (((mpoly->flag & ME_FACE_SEL) == 0) == unselected) { mpoly->flag |= ME_HIDE; @@ -136,8 +131,6 @@ void paintface_hide(bContext *C, Object *ob, const bool unselected) if (mpoly->flag & ME_HIDE) { mpoly->flag &= ~ME_FACE_SEL; } - - mpoly++; } BKE_mesh_flush_hidden_from_polys(me); @@ -147,23 +140,17 @@ void paintface_hide(bContext *C, Object *ob, const bool unselected) void paintface_reveal(bContext *C, Object *ob, const bool select) { - Mesh *me; - MPoly *mpoly; - int a; - - me = BKE_mesh_from_object(ob); - if (me == NULL || me->totpoly == 0) { + Mesh *me = BKE_mesh_from_object(ob); + if (me == nullptr || me->totpoly == 0) { return; } - mpoly = me->mpoly; - a = me->totpoly; - while (a--) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mpoly = &me->mpoly[i]; if (mpoly->flag & ME_HIDE) { SET_FLAG_FROM_TEST(mpoly->flag, select, ME_FACE_SEL); mpoly->flag &= ~ME_HIDE; } - mpoly++; } BKE_mesh_flush_hidden_from_polys(me); @@ -175,9 +162,6 @@ void paintface_reveal(bContext *C, Object *ob, const bool select) static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bool select) { - MPoly *mp; - MLoop *ml; - int a, b; bool do_it = true; bool mark = false; @@ -186,20 +170,20 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo if (index != (uint)-1) { /* only put face under cursor in array */ - mp = &me->mpoly[index]; + MPoly *mp = &me->mpoly[index]; BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart); BLI_BITMAP_ENABLE(poly_tag, index); } else { /* fill array by selection */ - mp = me->mpoly; - for (a = 0; a < me->totpoly; a++, mp++) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mp = &me->mpoly[i]; if (mp->flag & ME_HIDE) { /* pass */ } else if (mp->flag & ME_FACE_SEL) { BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart); - BLI_BITMAP_ENABLE(poly_tag, a); + BLI_BITMAP_ENABLE(poly_tag, i); } } } @@ -208,17 +192,17 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo do_it = false; /* expand selection */ - mp = me->mpoly; - for (a = 0; a < me->totpoly; a++, mp++) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mp = &me->mpoly[i]; if (mp->flag & ME_HIDE) { continue; } - if (!BLI_BITMAP_TEST(poly_tag, a)) { + if (!BLI_BITMAP_TEST(poly_tag, i)) { mark = false; - ml = me->mloop + mp->loopstart; - for (b = 0; b < mp->totloop; b++, ml++) { + MLoop *ml = me->mloop + mp->loopstart; + for (int b = 0; b < mp->totloop; b++, ml++) { if ((me->medge[ml->e].flag & ME_SEAM) == 0) { if (BLI_BITMAP_TEST(edge_tag, ml->e)) { mark = true; @@ -228,7 +212,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo } if (mark) { - BLI_BITMAP_ENABLE(poly_tag, a); + BLI_BITMAP_ENABLE(poly_tag, i); BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart); do_it = true; } @@ -238,8 +222,9 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo MEM_freeN(edge_tag); - for (a = 0, mp = me->mpoly; a < me->totpoly; a++, mp++) { - if (BLI_BITMAP_TEST(poly_tag, a)) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mp = &me->mpoly[i]; + if (BLI_BITMAP_TEST(poly_tag, i)) { SET_FLAG_FROM_TEST(mp->flag, select, ME_FACE_SEL); } } @@ -249,11 +234,10 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const bool select) { - Mesh *me; uint index = (uint)-1; - me = BKE_mesh_from_object(ob); - if (me == NULL || me->totpoly == 0) { + Mesh *me = BKE_mesh_from_object(ob); + if (me == nullptr || me->totpoly == 0) { return; } @@ -270,34 +254,27 @@ void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const b bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool flush_flags) { - Mesh *me; - MPoly *mpoly; - int a; - - me = BKE_mesh_from_object(ob); - if (me == NULL) { + Mesh *me = BKE_mesh_from_object(ob); + if (me == nullptr) { return false; } if (action == SEL_TOGGLE) { action = SEL_SELECT; - mpoly = me->mpoly; - a = me->totpoly; - while (a--) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mpoly = &me->mpoly[i]; if ((mpoly->flag & ME_HIDE) == 0 && mpoly->flag & ME_FACE_SEL) { action = SEL_DESELECT; break; } - mpoly++; } } bool changed = false; - mpoly = me->mpoly; - a = me->totpoly; - while (a--) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mpoly = &me->mpoly[i]; if ((mpoly->flag & ME_HIDE) == 0) { switch (action) { case SEL_SELECT: @@ -318,7 +295,6 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl break; } } - mpoly++; } if (changed) { @@ -331,30 +307,25 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl bool paintface_minmax(Object *ob, float r_min[3], float r_max[3]) { - const Mesh *me; - const MPoly *mp; - const MLoop *ml; - const MVert *mvert; - int a, b; bool ok = false; float vec[3], bmat[3][3]; - me = BKE_mesh_from_object(ob); + const Mesh *me = BKE_mesh_from_object(ob); if (!me || !me->mloopuv) { return ok; } + const MVert *mvert = me->mvert; copy_m3_m4(bmat, ob->obmat); - mvert = me->mvert; - mp = me->mpoly; - for (a = me->totpoly; a > 0; a--, mp++) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mp = &me->mpoly[i]; if (mp->flag & ME_HIDE || !(mp->flag & ME_FACE_SEL)) { continue; } - ml = me->mloop + mp->loopstart; - for (b = 0; b < mp->totloop; b++, ml++) { + const MLoop *ml = me->mloop + mp->loopstart; + for (int b = 0; b < mp->totloop; b++, ml++) { mul_v3_m3v3(vec, bmat, mvert[ml->v].co); add_v3_v3v3(vec, vec, ob->obmat[3]); minmax_v3v3_v3(r_min, r_max, vec); @@ -366,19 +337,18 @@ bool paintface_minmax(Object *ob, float r_min[3], float r_max[3]) return ok; } -bool paintface_mouse_select(struct bContext *C, +bool paintface_mouse_select(bContext *C, const int mval[2], - const struct SelectPick_Params *params, + const SelectPick_Params *params, Object *ob) { - Mesh *me; - MPoly *mpoly_sel = NULL; + MPoly *mpoly_sel = nullptr; uint index; bool changed = false; bool found = false; /* Get the face under the cursor */ - me = BKE_mesh_from_object(ob); + Mesh *me = BKE_mesh_from_object(ob); if (ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) { if (index < me->totpoly) { @@ -444,11 +414,11 @@ void paintvert_flush_flags(Object *ob) Mesh *me = BKE_mesh_from_object(ob); Mesh *me_eval = BKE_object_get_evaluated_mesh(ob); MVert *mvert_eval, *mv; - const int *index_array = NULL; + const int *index_array = nullptr; int totvert; int i; - if (me == NULL) { + if (me == nullptr) { return; } @@ -456,11 +426,11 @@ void paintvert_flush_flags(Object *ob) * since this could become slow for realtime updates (circle-select for eg) */ BKE_mesh_flush_select_from_verts(me); - if (me_eval == NULL) { + if (me_eval == nullptr) { return; } - index_array = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX); + index_array = (const int *)CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX); mvert_eval = me_eval->mvert; totvert = me_eval->totvert; @@ -485,41 +455,34 @@ void paintvert_flush_flags(Object *ob) BKE_mesh_batch_cache_dirty_tag(me, BKE_MESH_BATCH_DIRTY_ALL); } -void paintvert_tag_select_update(struct bContext *C, struct Object *ob) +void paintvert_tag_select_update(bContext *C, Object *ob) { - DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); + DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data); } bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags) { - Mesh *me; - MVert *mvert; - int a; - - me = BKE_mesh_from_object(ob); - if (me == NULL) { + Mesh *me = BKE_mesh_from_object(ob); + if (me == nullptr) { return false; } if (action == SEL_TOGGLE) { action = SEL_SELECT; - mvert = me->mvert; - a = me->totvert; - while (a--) { + for (int i = 0; i < me->totvert; i++) { + MVert *mvert = &me->mvert[i]; if ((mvert->flag & ME_HIDE) == 0 && mvert->flag & SELECT) { action = SEL_DESELECT; break; } - mvert++; } } bool changed = false; - mvert = me->mvert; - a = me->totvert; - while (a--) { + for (int i = 0; i < me->totvert; i++) { + MVert *mvert = &me->mvert[i]; if ((mvert->flag & ME_HIDE) == 0) { switch (action) { case SEL_SELECT: @@ -540,7 +503,6 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags) break; } } - mvert++; } if (changed) { @@ -565,11 +527,8 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags) void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags) { Mesh *me = BKE_mesh_from_object(ob); - MVert *mv; - MDeformVert *dv; - int a, tot; - if (me == NULL || me->dvert == NULL) { + if (me == nullptr || me->dvert == nullptr) { return; } @@ -577,12 +536,11 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags) paintvert_deselect_all_visible(ob, SEL_DESELECT, false); } - dv = me->dvert; - tot = me->totvert; - - for (a = 0, mv = me->mvert; a < tot; a++, mv++, dv++) { + for (int i = 0; i < me->totvert; i++) { + MVert *mv = &me->mvert[i]; + MDeformVert *dv = &me->dvert[i]; if ((mv->flag & ME_HIDE) == 0) { - if (dv->dw == NULL) { + if (dv->dw == nullptr) { /* if null weight then not grouped */ mv->flag |= SELECT; } diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 5d8cf176b87..5de7681a1e6 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -198,7 +198,7 @@ typedef struct KnifeObjectInfo { * Optionally allocate triangle indices, these are needed for non-interactive knife * projection as multiple cuts are made without the BVH being updated. * Using these indices the it's possible to access `cagecos` even if the face has been cut - * and the loops in `em->looptris` no longer refer to the original triangles, see: + * and the loops in `em->looptris` no longer refer to the original triangles, see: T97153. */ const int (*tri_indices)[3]; diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index c3d5f33705c..c7c7e5cf2f8 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -9,6 +9,7 @@ #include "DNA_key_types.h" #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "BLI_alloca.h" @@ -19,6 +20,7 @@ #include "BKE_DerivedMesh.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_editmesh_bvh.h" #include "BKE_global.h" diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.cc index d11f0b490c1..ae06cf4c3bd 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.cc @@ -13,7 +13,7 @@ #include "DNA_scene_types.h" #include "DNA_view3d_types.h" -#include "BLI_alloca.h" +#include "BLI_array.hh" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -43,10 +43,12 @@ #include "mesh_intern.h" /* own include */ +using blender::Array; + static CustomData *mesh_customdata_get_type(Mesh *me, const char htype, int *r_tot) { CustomData *data; - BMesh *bm = (me->edit_mesh) ? me->edit_mesh->bm : NULL; + BMesh *bm = (me->edit_mesh) ? me->edit_mesh->bm : nullptr; int tot; switch (htype) { @@ -93,7 +95,7 @@ static CustomData *mesh_customdata_get_type(Mesh *me, const char htype, int *r_t default: BLI_assert(0); tot = 0; - data = NULL; + data = nullptr; break; } @@ -172,7 +174,7 @@ static void mesh_uv_reset_array(float **fuv, const int len) static void mesh_uv_reset_bmface(BMFace *f, const int cd_loop_uv_offset) { - float **fuv = BLI_array_alloca(fuv, f->len); + Array<float *, BM_DEFAULT_NGON_STACK_SIZE> fuv(f->len); BMIter liter; BMLoop *l; int i; @@ -181,21 +183,21 @@ static void mesh_uv_reset_bmface(BMFace *f, const int cd_loop_uv_offset) fuv[i] = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset))->uv; } - mesh_uv_reset_array(fuv, f->len); + mesh_uv_reset_array(fuv.data(), f->len); } static void mesh_uv_reset_mface(MPoly *mp, MLoopUV *mloopuv) { - float **fuv = BLI_array_alloca(fuv, mp->totloop); + Array<float *, BM_DEFAULT_NGON_STACK_SIZE> fuv(mp->totloop); for (int i = 0; i < mp->totloop; i++) { fuv[i] = mloopuv[mp->loopstart + i].uv; } - mesh_uv_reset_array(fuv, mp->totloop); + mesh_uv_reset_array(fuv.data(), mp->totloop); } -void ED_mesh_uv_loop_reset_ex(struct Mesh *me, const int layernum) +void ED_mesh_uv_loop_reset_ex(Mesh *me, const int layernum) { BMEditMesh *em = me->edit_mesh; @@ -219,7 +221,7 @@ void ED_mesh_uv_loop_reset_ex(struct Mesh *me, const int layernum) else { /* Collect Mesh UVs */ BLI_assert(CustomData_has_layer(&me->ldata, CD_MLOOPUV)); - MLoopUV *mloopuv = CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, layernum); + MLoopUV *mloopuv = (MLoopUV *)CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, layernum); for (int i = 0; i < me->totpoly; i++) { mesh_uv_reset_mface(&me->mpoly[i], mloopuv); @@ -229,7 +231,7 @@ void ED_mesh_uv_loop_reset_ex(struct Mesh *me, const int layernum) DEG_id_tag_update(&me->id, 0); } -void ED_mesh_uv_loop_reset(struct bContext *C, struct Mesh *me) +void ED_mesh_uv_loop_reset(bContext *C, Mesh *me) { /* could be ldata or pdata */ CustomData *ldata = GET_CD_DATA(me, ldata); @@ -284,7 +286,7 @@ int ED_mesh_uv_texture_add( is_init = true; } else { - CustomData_add_layer_named(&me->ldata, CD_MLOOPUV, CD_DEFAULT, NULL, me->totloop, name); + CustomData_add_layer_named(&me->ldata, CD_MLOOPUV, CD_DEFAULT, nullptr, me->totloop, name); } if (active_set || layernum_dst == 0) { @@ -305,7 +307,7 @@ int ED_mesh_uv_texture_add( return layernum_dst; } -void ED_mesh_uv_texture_ensure(struct Mesh *me, const char *name) +void ED_mesh_uv_texture_ensure(Mesh *me, const char *name) { BMEditMesh *em; int layernum_dst; @@ -315,13 +317,13 @@ void ED_mesh_uv_texture_ensure(struct Mesh *me, const char *name) layernum_dst = CustomData_number_of_layers(&em->bm->ldata, CD_MLOOPUV); if (layernum_dst == 0) { - ED_mesh_uv_texture_add(me, name, true, true, NULL); + ED_mesh_uv_texture_add(me, name, true, true, nullptr); } } else { layernum_dst = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); if (layernum_dst == 0) { - ED_mesh_uv_texture_add(me, name, true, true, NULL); + ED_mesh_uv_texture_add(me, name, true, true, nullptr); } } } @@ -333,7 +335,7 @@ bool ED_mesh_uv_texture_remove_index(Mesh *me, const int n) int index; index = CustomData_get_layer_index_n(ldata, CD_MLOOPUV, n); - cdlu = (index == -1) ? NULL : &ldata->layers[index]; + cdlu = (index == -1) ? nullptr : &ldata->layers[index]; if (!cdlu) { return false; @@ -409,7 +411,7 @@ int ED_mesh_color_add( } else { CustomData_add_layer_named( - &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, NULL, me->totloop, name); + &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, nullptr, me->totloop, name); } if (active_set || layernum == 0) { @@ -425,14 +427,14 @@ int ED_mesh_color_add( return layernum; } -bool ED_mesh_color_ensure(struct Mesh *me, const char *name) +bool ED_mesh_color_ensure(Mesh *me, const char *name) { - BLI_assert(me->edit_mesh == NULL); + BLI_assert(me->edit_mesh == nullptr); CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); if (!layer) { CustomData_add_layer_named( - &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, NULL, me->totloop, name); + &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, nullptr, me->totloop, name); layer = me->ldata.layers + CustomData_get_layer_index(&me->ldata, CD_PROP_BYTE_COLOR); BKE_id_attributes_active_color_set(&me->id, layer); @@ -441,7 +443,7 @@ bool ED_mesh_color_ensure(struct Mesh *me, const char *name) DEG_id_tag_update(&me->id, 0); - return (layer != NULL); + return (layer != nullptr); } bool ED_mesh_color_remove_index(Mesh *me, const int n) @@ -451,7 +453,7 @@ bool ED_mesh_color_remove_index(Mesh *me, const int n) int index; index = CustomData_get_layer_index_n(ldata, CD_PROP_BYTE_COLOR, n); - cdl = (index == -1) ? NULL : &ldata->layers[index]; + cdl = (index == -1) ? nullptr : &ldata->layers[index]; if (!cdl) { return false; @@ -487,7 +489,7 @@ bool ED_mesh_color_remove_named(Mesh *me, const char *name) static bool layers_poll(bContext *C) { Object *ob = ED_object_context(C); - ID *data = (ob) ? ob->data : NULL; + ID *data = (ob) ? static_cast<ID *>(ob->data) : nullptr; return (ob && !ID_IS_LINKED(ob) && !ID_IS_OVERRIDE_LIBRARY(ob) && ob->type == OB_MESH && data && !ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data)); } @@ -501,7 +503,7 @@ static bool sculpt_vertex_color_remove_poll(bContext *C) } Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); CustomData *vdata = GET_CD_DATA(me, vdata); const int active = CustomData_get_active_layer(vdata, CD_PROP_COLOR); if (active != -1) { @@ -549,12 +551,14 @@ int ED_mesh_sculpt_color_add( } if (CustomData_has_layer(&me->vdata, CD_PROP_COLOR) && do_init) { - MPropCol *color_data = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); + const MPropCol *color_data = (const MPropCol *)CustomData_get_layer(&me->vdata, + CD_PROP_COLOR); CustomData_add_layer_named( - &me->vdata, CD_PROP_COLOR, CD_DUPLICATE, color_data, me->totvert, name); + &me->vdata, CD_PROP_COLOR, CD_DUPLICATE, (MPropCol *)color_data, me->totvert, name); } else { - CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, me->totvert, name); + CustomData_add_layer_named( + &me->vdata, CD_PROP_COLOR, CD_DEFAULT, nullptr, me->totvert, name); } if (active_set || layernum == 0) { @@ -570,18 +574,18 @@ int ED_mesh_sculpt_color_add( return layernum; } -bool ED_mesh_sculpt_color_ensure(struct Mesh *me, const char *name) +bool ED_mesh_sculpt_color_ensure(Mesh *me, const char *name) { - BLI_assert(me->edit_mesh == NULL); + BLI_assert(me->edit_mesh == nullptr); if (me->totvert && !CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) { - CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, me->totvert, name); + CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, nullptr, me->totvert, name); BKE_mesh_update_customdata_pointers(me, true); } DEG_id_tag_update(&me->id, 0); - return (me->mloopcol != NULL); + return (me->mloopcol != nullptr); } bool ED_mesh_sculpt_color_remove_index(Mesh *me, const int n) @@ -591,7 +595,7 @@ bool ED_mesh_sculpt_color_remove_index(Mesh *me, const int n) int index; index = CustomData_get_layer_index_n(vdata, CD_PROP_COLOR, n); - cdl = (index == -1) ? NULL : &vdata->layers[index]; + cdl = (index == -1) ? nullptr : &vdata->layers[index]; if (!cdl) { return false; @@ -631,7 +635,7 @@ static bool uv_texture_remove_poll(bContext *C) } Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); CustomData *ldata = GET_CD_DATA(me, ldata); const int active = CustomData_get_active_layer(ldata, CD_MLOOPUV); if (active != -1) { @@ -644,16 +648,16 @@ static bool uv_texture_remove_poll(bContext *C) static int mesh_uv_texture_add_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); - if (ED_mesh_uv_texture_add(me, NULL, true, true, op->reports) == -1) { + if (ED_mesh_uv_texture_add(me, nullptr, true, true, op->reports) == -1) { return OPERATOR_CANCELLED; } if (ob->mode & OB_MODE_TEXTURE_PAINT) { Scene *scene = CTX_data_scene(C); - ED_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + ED_paint_proj_mesh_data_check(scene, ob, nullptr, nullptr, nullptr, nullptr); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr); } return OPERATOR_FINISHED; @@ -677,7 +681,7 @@ void MESH_OT_uv_texture_add(wmOperatorType *ot) static int mesh_uv_texture_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (!ED_mesh_uv_texture_remove_active(me)) { return OPERATOR_CANCELLED; @@ -685,8 +689,8 @@ static int mesh_uv_texture_remove_exec(bContext *C, wmOperator *UNUSED(op)) if (ob->mode & OB_MODE_TEXTURE_PAINT) { Scene *scene = CTX_data_scene(C); - ED_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + ED_paint_proj_mesh_data_check(scene, ob, nullptr, nullptr, nullptr, nullptr); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr); } return OPERATOR_FINISHED; @@ -716,7 +720,7 @@ static bool vertex_color_remove_poll(bContext *C) } Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); CustomData *ldata = GET_CD_DATA(me, ldata); const int active = CustomData_get_active_layer(ldata, CD_PROP_BYTE_COLOR); if (active != -1) { @@ -729,9 +733,9 @@ static bool vertex_color_remove_poll(bContext *C) static int mesh_vertex_color_add_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); - if (ED_mesh_color_add(me, NULL, true, true, op->reports) == -1) { + if (ED_mesh_color_add(me, nullptr, true, true, op->reports) == -1) { return OPERATOR_CANCELLED; } @@ -756,7 +760,7 @@ void MESH_OT_vertex_color_add(wmOperatorType *ot) static int mesh_vertex_color_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (!ED_mesh_color_remove_active(me)) { return OPERATOR_CANCELLED; @@ -785,9 +789,9 @@ void MESH_OT_vertex_color_remove(wmOperatorType *ot) static int mesh_sculpt_vertex_color_add_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); - if (ED_mesh_sculpt_color_add(me, NULL, true, true, op->reports) == -1) { + if (ED_mesh_sculpt_color_add(me, nullptr, true, true, op->reports) == -1) { return OPERATOR_CANCELLED; } @@ -812,7 +816,7 @@ void MESH_OT_sculpt_vertex_color_add(wmOperatorType *ot) static int mesh_sculpt_vertex_color_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (!ED_mesh_sculpt_color_remove_active(me)) { return OPERATOR_CANCELLED; @@ -868,7 +872,7 @@ static bool mesh_customdata_mask_clear_poll(bContext *C) { Object *ob = ED_object_context(C); if (ob && ob->type == OB_MESH) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); /* special case - can't run this if we're in sculpt mode */ if (ob->mode & OB_MODE_SCULPT) { @@ -925,7 +929,7 @@ static int mesh_customdata_skin_state(bContext *C) Object *ob = ED_object_context(C); if (ob && ob->type == OB_MESH) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (!ID_IS_LINKED(me) && !ID_IS_OVERRIDE_LIBRARY(me)) { CustomData *data = GET_CD_DATA(me, vdata); return CustomData_has_layer(data, CD_MVERT_SKIN); @@ -942,7 +946,7 @@ static bool mesh_customdata_skin_add_poll(bContext *C) static int mesh_customdata_skin_add_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BKE_mesh_ensure_skin_customdata(me); @@ -1025,7 +1029,7 @@ static int mesh_customdata_custom_splitnormals_add_exec(bContext *C, wmOperator me->smoothresh); } - CustomData_add_layer(data, CD_CUSTOMLOOPNORMAL, CD_DEFAULT, NULL, me->totloop); + CustomData_add_layer(data, CD_CUSTOMLOOPNORMAL, CD_DEFAULT, nullptr, me->totloop); } DEG_id_tag_update(&me->id, 0); @@ -1057,7 +1061,7 @@ static int mesh_customdata_custom_splitnormals_clear_exec(bContext *C, wmOperato if (BKE_mesh_has_custom_loop_normals(me)) { BMEditMesh *em = me->edit_mesh; - if (em != NULL && em->bm->lnor_spacearr != NULL) { + if (em != nullptr && em->bm->lnor_spacearr != nullptr) { BKE_lnor_spacearr_clear(em->bm->lnor_spacearr); } return mesh_customdata_clear_exec__internal(C, BM_LOOP, CD_CUSTOMLOOPNORMAL); @@ -1114,7 +1118,7 @@ static void mesh_add_verts(Mesh *mesh, int len) CustomData_copy_data(&mesh->vdata, &vdata, 0, 0, mesh->totvert); if (!CustomData_has_layer(&vdata, CD_MVERT)) { - CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert); + CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, nullptr, totvert); } CustomData_free(&mesh->vdata, mesh->totvert); @@ -1152,7 +1156,7 @@ static void mesh_add_edges(Mesh *mesh, int len) CustomData_copy_data(&mesh->edata, &edata, 0, 0, mesh->totedge); if (!CustomData_has_layer(&edata, CD_MEDGE)) { - CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge); + CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, nullptr, totedge); } CustomData_free(&mesh->edata, mesh->totedge); @@ -1186,7 +1190,7 @@ static void mesh_add_loops(Mesh *mesh, int len) CustomData_copy_data(&mesh->ldata, &ldata, 0, 0, mesh->totloop); if (!CustomData_has_layer(&ldata, CD_MLOOP)) { - CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloop); + CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, nullptr, totloop); } BKE_mesh_runtime_clear_cache(mesh); @@ -1215,7 +1219,7 @@ static void mesh_add_polys(Mesh *mesh, int len) CustomData_copy_data(&mesh->pdata, &pdata, 0, 0, mesh->totpoly); if (!CustomData_has_layer(&pdata, CD_MPOLY)) { - CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpoly); + CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, nullptr, totpoly); } CustomData_free(&mesh->pdata, mesh->totpoly); @@ -1413,21 +1417,21 @@ void ED_mesh_report_mirror(wmOperator *op, int totmirr, int totfail) ED_mesh_report_mirror_ex(op, totmirr, totfail, SCE_SELECT_VERTEX); } -Mesh *ED_mesh_context(struct bContext *C) +Mesh *ED_mesh_context(bContext *C) { - Mesh *mesh = CTX_data_pointer_get_type(C, "mesh", &RNA_Mesh).data; - if (mesh != NULL) { + Mesh *mesh = static_cast<Mesh *>(CTX_data_pointer_get_type(C, "mesh", &RNA_Mesh).data); + if (mesh != nullptr) { return mesh; } Object *ob = ED_object_active_context(C); - if (ob == NULL) { - return NULL; + if (ob == nullptr) { + return nullptr; } ID *data = (ID *)ob->data; - if (data == NULL || GS(data->name) != ID_ME) { - return NULL; + if (data == nullptr || GS(data->name) != ID_ME) { + return nullptr; } return (Mesh *)data; diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index b39a0b90f6d..196c4957d84 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -9,6 +9,10 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + struct BMEditMesh; struct BMElem; struct BMOperator; @@ -301,7 +305,7 @@ void MESH_OT_mark_freestyle_edge(struct wmOperatorType *ot); void MESH_OT_mark_freestyle_face(struct wmOperatorType *ot); #endif -/* *** mesh_data.c *** */ +/* *** mesh_data.cc *** */ void MESH_OT_uv_texture_add(struct wmOperatorType *ot); void MESH_OT_uv_texture_remove(struct wmOperatorType *ot); @@ -314,3 +318,7 @@ void MESH_OT_customdata_skin_add(struct wmOperatorType *ot); void MESH_OT_customdata_skin_clear(struct wmOperatorType *ot); void MESH_OT_customdata_custom_splitnormals_add(struct wmOperatorType *ot); void MESH_OT_customdata_custom_splitnormals_clear(struct wmOperatorType *ot); + +#ifdef __cplusplus +} +#endif
\ No newline at end of file diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.cc index 9575fde68f0..b104820733b 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.cc @@ -21,10 +21,8 @@ #include "DNA_view3d_types.h" #include "DNA_workspace_types.h" -#include "BLI_blenlib.h" -#include "BLI_math.h" - #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_key.h" @@ -91,7 +89,7 @@ static void join_mesh_single(Depsgraph *depsgraph, { int a, b; - Mesh *me = ob_src->data; + Mesh *me = static_cast<Mesh *>(ob_src->data); MVert *mvert = *mvert_pp; MEdge *medge = *medge_pp; MLoop *mloop = *mloop_pp; @@ -106,12 +104,13 @@ static void join_mesh_single(Depsgraph *depsgraph, CustomData_copy_data_named(&me->vdata, vdata, 0, *vertofs, me->totvert); /* vertex groups */ - MDeformVert *dvert = CustomData_get(vdata, *vertofs, CD_MDEFORMVERT); - MDeformVert *dvert_src = CustomData_get(&me->vdata, 0, CD_MDEFORMVERT); + MDeformVert *dvert = (MDeformVert *)CustomData_get(vdata, *vertofs, CD_MDEFORMVERT); + const MDeformVert *dvert_src = (const MDeformVert *)CustomData_get( + &me->vdata, 0, CD_MDEFORMVERT); /* Remap to correct new vgroup indices, if needed. */ if (dvert_src) { - BLI_assert(dvert != NULL); + BLI_assert(dvert != nullptr); /* Build src to merged mapping of vgroup indices. */ int *vgroup_index_map; @@ -120,7 +119,7 @@ static void join_mesh_single(Depsgraph *depsgraph, ob_src, ob_dst, &vgroup_index_map_len); BKE_object_defgroup_index_map_apply( dvert, me->totvert, vgroup_index_map, vgroup_index_map_len); - if (vgroup_index_map != NULL) { + if (vgroup_index_map != nullptr) { MEM_freeN(vgroup_index_map); } } @@ -150,11 +149,11 @@ static void join_mesh_single(Depsgraph *depsgraph, float(*cos)[3] = ((float(*)[3])kb->data) + *vertofs; /* Check if this mesh has such a shape-key. */ - KeyBlock *okb = me->key ? BKE_keyblock_find_name(me->key, kb->name) : NULL; + KeyBlock *okb = me->key ? BKE_keyblock_find_name(me->key, kb->name) : nullptr; if (okb) { /* copy this mesh's shape-key to the destination shape-key * (need to transform first) */ - float(*ocos)[3] = okb->data; + float(*ocos)[3] = static_cast<float(*)[3]>(okb->data); for (a = 0; a < me->totvert; a++, cos++, ocos++) { copy_v3_v3(*cos, *ocos); mul_m4_v3(cmat, *cos); @@ -180,10 +179,10 @@ static void join_mesh_single(Depsgraph *depsgraph, float(*cos)[3] = ((float(*)[3])kb->data) + *vertofs; /* Check if this was one of the original shape-keys. */ - KeyBlock *okb = nkey ? BKE_keyblock_find_name(nkey, kb->name) : NULL; + KeyBlock *okb = nkey ? BKE_keyblock_find_name(nkey, kb->name) : nullptr; if (okb) { /* copy this mesh's shape-key to the destination shape-key */ - float(*ocos)[3] = okb->data; + float(*ocos)[3] = static_cast<float(*)[3]>(okb->data); for (a = 0; a < me->totvert; a++, cos++, ocos++) { copy_v3_v3(*cos, *ocos); } @@ -254,17 +253,17 @@ static void join_mesh_single(Depsgraph *depsgraph, } /* Face maps. */ - int *fmap = CustomData_get(pdata, *polyofs, CD_FACEMAP); - int *fmap_src = CustomData_get(&me->pdata, 0, CD_FACEMAP); + int *fmap = (int *)CustomData_get(pdata, *polyofs, CD_FACEMAP); + const int *fmap_src = (const int *)CustomData_get(&me->pdata, 0, CD_FACEMAP); /* Remap to correct new face-map indices, if needed. */ if (fmap_src) { - BLI_assert(fmap != NULL); + BLI_assert(fmap != nullptr); int *fmap_index_map; int fmap_index_map_len; fmap_index_map = BKE_object_facemap_index_map_create(ob_src, ob_dst, &fmap_index_map_len); BKE_object_facemap_index_map_apply(fmap, me->totpoly, fmap_index_map, fmap_index_map_len); - if (fmap_index_map != NULL) { + if (fmap_index_map != nullptr) { MEM_freeN(fmap_index_map); } } @@ -290,7 +289,7 @@ static void mesh_join_offset_face_sets_ID(const Mesh *mesh, int *face_set_offset return; } - int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + int *face_sets = (int *)CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); if (!face_sets) { return; } @@ -317,20 +316,18 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); - Material **matar = NULL, *ma; + Material **matar = nullptr, *ma; Mesh *me; - MVert *mvert = NULL; - MEdge *medge = NULL; - MPoly *mpoly = NULL; - MLoop *mloop = NULL; - Key *key, *nkey = NULL; - KeyBlock *kb, *kbn; + MVert *mvert = nullptr; + MEdge *medge = nullptr; + MPoly *mpoly = nullptr; + MLoop *mloop = nullptr; + Key *key, *nkey = nullptr; float imat[4][4]; int a, b, totcol, totmat = 0, totedge = 0, totvert = 0; - int totloop = 0, totpoly = 0, vertofs, *matmap = NULL; + int totloop = 0, totpoly = 0, vertofs, *matmap = nullptr; int i, haskey = 0, edgeofs, loopofs, polyofs; bool ok = false, join_parent = false; - bDeformGroup *dg, *odg; CustomData vdata, edata, fdata, ldata, pdata; if (ob->mode & OB_MODE_EDIT) { @@ -349,7 +346,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* count & check */ CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) { if (ob_iter->type == OB_MESH) { - me = ob_iter->data; + me = static_cast<Mesh *>(ob_iter->data); totvert += me->totvert; totedge += me->totedge; @@ -361,7 +358,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) ok = true; } - if ((ob->parent != NULL) && (ob_iter == ob->parent)) { + if ((ob->parent != nullptr) && (ob_iter == ob->parent)) { join_parent = true; } @@ -376,7 +373,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* Apply parent transform if the active object's parent was joined to it. * NOTE: This doesn't apply recursive parenting. */ if (join_parent) { - ob->parent = NULL; + ob->parent = nullptr; BKE_object_apply_mat4_ex(ob, ob->obmat, ob->parent, ob->parentinv, false); } @@ -416,8 +413,8 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* new material indices and material array */ if (totmat) { - matar = MEM_callocN(sizeof(*matar) * totmat, "join_mesh matar"); - matmap = MEM_callocN(sizeof(*matmap) * totmat, "join_mesh matmap"); + matar = static_cast<Material **>(MEM_callocN(sizeof(*matar) * totmat, __func__)); + matmap = static_cast<int *>(MEM_callocN(sizeof(*matmap) * totmat, __func__)); } totcol = ob->totcol; @@ -438,7 +435,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) nkey = (Key *)BKE_id_copy(bmain, &key->id); /* for all keys in old block, clear data-arrays */ - for (kb = key->block.first; kb; kb = kb->next) { + LISTBASE_FOREACH (KeyBlock *, kb, &key->block) { if (kb->data) { MEM_freeN(kb->data); } @@ -462,13 +459,14 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) { /* only act if a mesh, and not the one we're joining to */ if ((ob != ob_iter) && (ob_iter->type == OB_MESH)) { - me = ob_iter->data; + me = static_cast<Mesh *>(ob_iter->data); /* Join this object's vertex groups to the base one's */ - for (dg = me->vertex_group_names.first; dg; dg = dg->next) { + LISTBASE_FOREACH (bDeformGroup *, dg, &me->vertex_group_names) { /* See if this group exists in the object (if it doesn't, add it to the end) */ if (!BKE_object_defgroup_find_name(ob, dg->name)) { - odg = MEM_mallocN(sizeof(bDeformGroup), "join deformGroup"); + bDeformGroup *odg = static_cast<bDeformGroup *>( + MEM_mallocN(sizeof(bDeformGroup), __func__)); memcpy(odg, dg, sizeof(bDeformGroup)); BLI_addtail(&mesh_active->vertex_group_names, odg); } @@ -481,8 +479,8 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* Join this object's face maps to the base one's. */ LISTBASE_FOREACH (bFaceMap *, fmap, &ob_iter->fmaps) { /* See if this group exists in the object (if it doesn't, add it to the end) */ - if (BKE_object_facemap_find_name(ob, fmap->name) == NULL) { - bFaceMap *fmap_new = MEM_mallocN(sizeof(bFaceMap), "join faceMap"); + if (BKE_object_facemap_find_name(ob, fmap->name) == nullptr) { + bFaceMap *fmap_new = static_cast<bFaceMap *>(MEM_mallocN(sizeof(bFaceMap), __func__)); memcpy(fmap_new, fmap, sizeof(bFaceMap)); BLI_addtail(&ob->fmaps, fmap_new); } @@ -522,13 +520,15 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) * check if destination mesh already has matching entries too. */ if (me->key && key) { /* for remapping KeyBlock.relative */ - int *index_map = MEM_mallocN(sizeof(int) * me->key->totkey, __func__); - KeyBlock **kb_map = MEM_mallocN(sizeof(KeyBlock *) * me->key->totkey, __func__); + int *index_map = static_cast<int *>( + MEM_mallocN(sizeof(int) * me->key->totkey, __func__)); + KeyBlock **kb_map = static_cast<KeyBlock **>( + MEM_mallocN(sizeof(KeyBlock *) * me->key->totkey, __func__)); - for (kb = me->key->block.first, i = 0; kb; kb = kb->next, i++) { + LISTBASE_FOREACH_INDEX (KeyBlock *, kb, &me->key->block, i) { BLI_assert(i < me->key->totkey); - kbn = BKE_keyblock_find_name(key, kb->name); + KeyBlock *kbn = BKE_keyblock_find_name(key, kb->name); /* if key doesn't exist in destination mesh, add it */ if (kbn) { index_map[i] = BLI_findindex(&key->block, kbn); @@ -549,7 +549,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) } /* remap relative index values */ - for (kb = me->key->block.first, i = 0; kb; kb = kb->next, i++) { + LISTBASE_FOREACH_INDEX (KeyBlock *, kb, &me->key->block, i) { /* sanity check, should always be true */ if (LIKELY(kb->relative < me->key->totkey)) { kb_map[i]->relative = index_map[kb->relative]; @@ -571,10 +571,10 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) CustomData_reset(&ldata); CustomData_reset(&pdata); - mvert = CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert); - medge = CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge); - mloop = CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloop); - mpoly = CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpoly); + mvert = (MVert *)CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, nullptr, totvert); + medge = (MEdge *)CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, nullptr, totedge); + mloop = (MLoop *)CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, nullptr, totloop); + mpoly = (MPoly *)CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, nullptr, totpoly); vertofs = 0; edgeofs = 0; @@ -661,7 +661,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* return to mesh we're merging to */ - me = ob->data; + me = static_cast<Mesh *>(ob->data); CustomData_free(&me->vdata, me->totvert); CustomData_free(&me->edata, me->totedge); @@ -703,8 +703,8 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) if (totcol) { me->mat = matar; - ob->mat = MEM_callocN(sizeof(*ob->mat) * totcol, "join obmatar"); - ob->matbits = MEM_callocN(sizeof(*ob->matbits) * totcol, "join obmatbits"); + ob->mat = static_cast<Material **>(MEM_callocN(sizeof(*ob->mat) * totcol, __func__)); + ob->matbits = static_cast<char *>(MEM_callocN(sizeof(*ob->matbits) * totcol, __func__)); MEM_freeN(matmap); } @@ -751,8 +751,8 @@ int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op) Object *ob_active = CTX_data_active_object(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Mesh *me = (Mesh *)ob_active->data; - Mesh *selme = NULL; - Mesh *me_deformed = NULL; + Mesh *selme = nullptr; + Mesh *me_deformed = nullptr; Key *key = me->key; KeyBlock *kb; bool ok = false, nonequal_verts = false; @@ -769,7 +769,7 @@ int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op) ok = true; } else { - nonequal_verts = 1; + nonequal_verts = true; } } } @@ -787,12 +787,12 @@ int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (key == NULL) { + if (key == nullptr) { key = me->key = BKE_key_add(bmain, (ID *)me); key->type = KEY_RELATIVE; /* first key added, so it was the basis. initialize it with the existing mesh */ - kb = BKE_keyblock_add(key, NULL); + kb = BKE_keyblock_add(key, nullptr); BKE_keyblock_convert_from_mesh(me, key, kb); } @@ -835,21 +835,21 @@ int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op) /** \name Mesh Topology Mirror API * \{ */ -static MirrTopoStore_t mesh_topo_store = {NULL, -1. - 1, -1}; +static MirrTopoStore_t mesh_topo_store = {nullptr, -1, -1, false}; BLI_INLINE void mesh_mirror_topo_table_get_meshes(Object *ob, Mesh *me_eval, Mesh **r_me_mirror, BMEditMesh **r_em_mirror) { - Mesh *me_mirror = NULL; - BMEditMesh *em_mirror = NULL; + Mesh *me_mirror = nullptr; + BMEditMesh *em_mirror = nullptr; - Mesh *me = ob->data; - if (me_eval != NULL) { + Mesh *me = static_cast<Mesh *>(ob->data); + if (me_eval != nullptr) { me_mirror = me_eval; } - else if (me->edit_mesh != NULL) { + else if (me->edit_mesh != nullptr) { em_mirror = me->edit_mesh; } else { @@ -892,7 +892,7 @@ static bool ed_mesh_mirror_topo_table_update(Object *ob, Mesh *me_eval) static int mesh_get_x_mirror_vert_spatial(Object *ob, Mesh *me_eval, int index) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); MVert *mvert = me_eval ? me_eval->mvert : me->mvert; float vec[3]; @@ -901,7 +901,7 @@ static int mesh_get_x_mirror_vert_spatial(Object *ob, Mesh *me_eval, int index) vec[1] = mvert->co[1]; vec[2] = mvert->co[2]; - return ED_mesh_mirror_spatial_table_lookup(ob, NULL, me_eval, vec); + return ED_mesh_mirror_spatial_table_lookup(ob, nullptr, me_eval, vec); } static int mesh_get_x_mirror_vert_topo(Object *ob, Mesh *mesh, int index) @@ -928,28 +928,25 @@ static BMVert *editbmesh_get_x_mirror_vert_spatial(Object *ob, BMEditMesh *em, c /* ignore nan verts */ if ((isfinite(co[0]) == false) || (isfinite(co[1]) == false) || (isfinite(co[2]) == false)) { - return NULL; + return nullptr; } vec[0] = -co[0]; vec[1] = co[1]; vec[2] = co[2]; - i = ED_mesh_mirror_spatial_table_lookup(ob, em, NULL, vec); + i = ED_mesh_mirror_spatial_table_lookup(ob, em, nullptr, vec); if (i != -1) { return BM_vert_at_index(em->bm, i); } - return NULL; + return nullptr; } -static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob, - struct BMEditMesh *em, - BMVert *eve, - int index) +static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob, BMEditMesh *em, BMVert *eve, int index) { intptr_t poinval; - if (!ed_mesh_mirror_topo_table_update(ob, NULL)) { - return NULL; + if (!ed_mesh_mirror_topo_table_update(ob, nullptr)) { + return nullptr; } if (index == -1) { @@ -965,7 +962,7 @@ static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob, } if (index == em->bm->totvert) { - return NULL; + return nullptr; } } @@ -974,15 +971,11 @@ static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob, if (poinval != -1) { return (BMVert *)(poinval); } - return NULL; + return nullptr; } -BMVert *editbmesh_get_x_mirror_vert(Object *ob, - struct BMEditMesh *em, - BMVert *eve, - const float co[3], - int index, - const bool use_topology) +BMVert *editbmesh_get_x_mirror_vert( + Object *ob, BMEditMesh *em, BMVert *eve, const float co[3], int index, const bool use_topology) { if (use_topology) { return editbmesh_get_x_mirror_vert_topo(ob, em, eve, index); @@ -992,7 +985,7 @@ BMVert *editbmesh_get_x_mirror_vert(Object *ob, int ED_mesh_mirror_get_vert(Object *ob, int index) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BMEditMesh *em = me->edit_mesh; bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; int index_mirr; @@ -1004,7 +997,7 @@ int ED_mesh_mirror_get_vert(Object *ob, int index) index_mirr = eve_mirr ? BM_elem_index_get(eve_mirr) : -1; } else { - index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, use_topology); + index_mirr = mesh_get_x_mirror_vert(ob, nullptr, index, use_topology); } return index_mirr; @@ -1021,7 +1014,7 @@ static float *editmesh_get_mirror_uv( /* ignore nan verts */ if (isnan(uv[0]) || !isfinite(uv[0]) || isnan(uv[1]) || !isfinite(uv[1])) { - return NULL; + return nullptr; } if (axis) { @@ -1061,14 +1054,14 @@ static float *editmesh_get_mirror_uv( } } - return NULL; + return nullptr; } #endif static uint mirror_facehash(const void *ptr) { - const MFace *mf = ptr; + const MFace *mf = static_cast<const MFace *>(ptr); uint v0, v1; if (mf->v4) { @@ -1121,21 +1114,21 @@ static bool mirror_facecmp(const void *a, const void *b) int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); MVert *mv, *mvert; MFace mirrormf, *mf, *hashmf, *mface; GHash *fhash; int *mirrorverts, *mirrorfaces; - BLI_assert(em == NULL); /* Does not work otherwise, currently... */ + BLI_assert(em == nullptr); /* Does not work otherwise, currently... */ const bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; const int totvert = me_eval ? me_eval->totvert : me->totvert; const int totface = me_eval ? me_eval->totface : me->totface; int a; - mirrorverts = MEM_callocN(sizeof(int) * totvert, "MirrorVerts"); - mirrorfaces = MEM_callocN(sizeof(int[2]) * totface, "MirrorFaces"); + mirrorverts = static_cast<int *>(MEM_callocN(sizeof(int) * totvert, "MirrorVerts")); + mirrorfaces = static_cast<int *>(MEM_callocN(sizeof(int[2]) * totface, "MirrorFaces")); mvert = me_eval ? me_eval->mvert : me->mvert; mface = me_eval ? me_eval->mface : me->mface; @@ -1165,7 +1158,7 @@ int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval) SWAP(uint, mirrormf.v2, mirrormf.v4); } - hashmf = BLI_ghash_lookup(fhash, &mirrormf); + hashmf = static_cast<MFace *>(BLI_ghash_lookup(fhash, &mirrormf)); if (hashmf) { mirrorfaces[a * 2] = hashmf - mface; mirrorfaces[a * 2 + 1] = mirror_facerotation(&mirrormf, hashmf); @@ -1175,7 +1168,7 @@ int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval) } } - BLI_ghash_free(fhash, NULL, NULL); + BLI_ghash_free(fhash, nullptr, nullptr); MEM_freeN(mirrorverts); return mirrorfaces; @@ -1186,7 +1179,7 @@ int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval) bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px, uint *r_index) { ViewContext vc; - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BLI_assert(me && GS(me->id.name) == ID_ME); @@ -1199,8 +1192,8 @@ bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px, ED_view3d_select_id_validate(&vc); if (dist_px) { - /* sample rect to increase chances of selecting, so that when clicking - * on an edge in the backbuf, we can still select a face */ + /* Sample rect to increase chances of selecting, so that when clicking + * on an edge in the back-buffer, we can still select a face. */ *r_index = DRW_select_buffer_find_nearest_to_point( vc.depsgraph, vc.region, vc.v3d, mval, 1, me->totpoly + 1, &dist_px); } @@ -1220,7 +1213,7 @@ bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px, static void ed_mesh_pick_face_vert__mpoly_find( /* context */ - struct ARegion *region, + ARegion *region, const float mval[2], /* mesh data (evaluated) */ const MPoly *mp, @@ -1250,14 +1243,14 @@ bool ED_mesh_pick_face_vert( { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); uint poly_index; - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BLI_assert(me && GS(me->id.name) == ID_ME); if (ED_mesh_pick_face(C, ob, mval, dist_px, &poly_index)) { Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - struct ARegion *region = CTX_wm_region(C); + ARegion *region = CTX_wm_region(C); /* derived mesh to find deformed locations */ Mesh *me_eval = mesh_get_eval_final( @@ -1266,14 +1259,13 @@ bool ED_mesh_pick_face_vert( int v_idx_best = ORIGINDEX_NONE; /* find the vert closest to 'mval' */ - const float mval_f[2] = {UNPACK2(mval)}; + const float mval_f[2] = {(float)mval[0], (float)mval[1]}; float len_best = FLT_MAX; MPoly *me_eval_mpoly; MLoop *me_eval_mloop; MVert *me_eval_mvert; uint me_eval_mpoly_len; - const int *index_mp_to_orig; me_eval_mpoly = me_eval->mpoly; me_eval_mloop = me_eval->mloop; @@ -1281,7 +1273,7 @@ bool ED_mesh_pick_face_vert( me_eval_mpoly_len = me_eval->totpoly; - index_mp_to_orig = CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX); + const int *index_mp_to_orig = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX); /* tag all verts using this face */ if (index_mp_to_orig) { @@ -1313,8 +1305,8 @@ bool ED_mesh_pick_face_vert( /* map 'dm -> me' r_index if possible */ if (v_idx_best != ORIGINDEX_NONE) { - const int *index_mv_to_orig; - index_mv_to_orig = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX); + const int *index_mv_to_orig = (const int *)CustomData_get_layer(&me_eval->vdata, + CD_ORIGINDEX); if (index_mv_to_orig) { v_idx_best = index_mv_to_orig[v_idx_best]; } @@ -1335,7 +1327,7 @@ bool ED_mesh_pick_face_vert( * * \return boolean true == Found */ -typedef struct VertPickData { +struct VertPickData { const MVert *mvert; const float *mval_f; /* [2] */ ARegion *region; @@ -1343,14 +1335,14 @@ typedef struct VertPickData { /* runtime */ float len_best; int v_idx_best; -} VertPickData; +}; static void ed_mesh_pick_vert__mapFunc(void *userData, int index, const float co[3], const float UNUSED(no[3])) { - VertPickData *data = userData; + VertPickData *data = static_cast<VertPickData *>(userData); if ((data->mvert[index].flag & ME_HIDE) == 0) { float sco[2]; @@ -1368,7 +1360,7 @@ bool ED_mesh_pick_vert( bContext *C, Object *ob, const int mval[2], uint dist_px, bool use_zbuf, uint *r_index) { ViewContext vc; - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BLI_assert(me && GS(me->id.name) == ID_ME); @@ -1405,16 +1397,16 @@ bool ED_mesh_pick_vert( /* derived mesh to find deformed locations */ Mesh *me_eval = mesh_get_eval_final(vc.depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH); ARegion *region = vc.region; - RegionView3D *rv3d = region->regiondata; + RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata); /* find the vert closest to 'mval' */ const float mval_f[2] = {(float)mval[0], (float)mval[1]}; - VertPickData data = {NULL}; + VertPickData data = {nullptr}; ED_view3d_init_mats_rv3d(ob, rv3d); - if (me_eval == NULL) { + if (me_eval == nullptr) { return false; } @@ -1440,7 +1432,7 @@ bool ED_mesh_pick_vert( MDeformVert *ED_mesh_active_dvert_get_em(Object *ob, BMVert **r_eve) { if (ob->mode & OB_MODE_EDIT && ob->type == OB_MESH) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (!BLI_listbase_is_empty(&me->vertex_group_names)) { BMesh *bm = me->edit_mesh->bm; const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT); @@ -1452,27 +1444,27 @@ MDeformVert *ED_mesh_active_dvert_get_em(Object *ob, BMVert **r_eve) if (r_eve) { *r_eve = eve; } - return BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); + return static_cast<MDeformVert *>(BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)); } } } } if (r_eve) { - *r_eve = NULL; + *r_eve = nullptr; } - return NULL; + return nullptr; } MDeformVert *ED_mesh_active_dvert_get_ob(Object *ob, int *r_index) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); int index = BKE_mesh_mselect_active_get(me, ME_VSEL); if (r_index) { *r_index = index; } - if (index == -1 || me->dvert == NULL) { - return NULL; + if (index == -1 || me->dvert == nullptr) { + return nullptr; } return me->dvert + index; } @@ -1481,14 +1473,14 @@ MDeformVert *ED_mesh_active_dvert_get_only(Object *ob) { if (ob->type == OB_MESH) { if (ob->mode & OB_MODE_EDIT) { - return ED_mesh_active_dvert_get_em(ob, NULL); + return ED_mesh_active_dvert_get_em(ob, nullptr); } - return ED_mesh_active_dvert_get_ob(ob, NULL); + return ED_mesh_active_dvert_get_ob(ob, nullptr); } - return NULL; + return nullptr; } -void EDBM_mesh_stats_multi(struct Object **objects, +void EDBM_mesh_stats_multi(Object **objects, const uint objects_len, int totelem[3], int totelem_sel[3]) diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index db8860efdd8..ac8ab834cb7 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -1888,7 +1888,7 @@ void OBJECT_OT_collection_external_asset_drop(wmOperatorType *ot) ED_object_add_generic_props(ot, false); - /* Important: Instancing option. Intentionally remembered across executions (no #PROP_SKIP_SAVE). + /* IMPORTANT: Instancing option. Intentionally remembered across executions (no #PROP_SKIP_SAVE). */ RNA_def_boolean(ot->srna, "use_instance", @@ -2116,6 +2116,22 @@ static int object_curves_empty_hair_add_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static bool object_curves_empty_hair_add_poll(bContext *C) +{ + if (!U.experimental.use_new_curves_type) { + return false; + } + if (!ED_operator_objectmode(C)) { + return false; + } + Object *ob = CTX_data_active_object(C); + if (ob == nullptr || ob->type != OB_MESH) { + CTX_wm_operator_poll_msg_set(C, "No active mesh object"); + return false; + } + return true; +} + void OBJECT_OT_curves_empty_hair_add(wmOperatorType *ot) { ot->name = "Add Empty Curves"; @@ -2123,7 +2139,7 @@ void OBJECT_OT_curves_empty_hair_add(wmOperatorType *ot) ot->idname = "OBJECT_OT_curves_empty_hair_add"; ot->exec = object_curves_empty_hair_add_exec; - ot->poll = object_curves_add_poll; + ot->poll = object_curves_empty_hair_add_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2762,9 +2778,32 @@ static const EnumPropertyItem convert_target_items[] = { "Point Cloud", "Point Cloud from Mesh objects"}, #endif + {OB_CURVES, "CURVES", ICON_OUTLINER_OB_CURVES, "Curves", "Curves from evaluated curve data"}, {0, nullptr, 0, nullptr, nullptr}, }; +static const EnumPropertyItem *convert_target_items_fn(bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + EnumPropertyItem *items = nullptr; + int items_num = 0; + for (const EnumPropertyItem *item = convert_target_items; item->identifier != nullptr; item++) { + if (item->value == OB_CURVES) { + if (U.experimental.use_new_curves_type) { + RNA_enum_item_add(&items, &items_num, item); + } + } + else { + RNA_enum_item_add(&items, &items_num, item); + } + } + RNA_enum_item_end(&items, &items_num); + *r_free = true; + return items; +} + static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob) { if (ob->runtime.curve_cache == nullptr) { @@ -3065,6 +3104,50 @@ static int object_convert_exec(bContext *C, wmOperator *op) } ob_gpencil->actcol = actcol; } + else if (target == OB_CURVES) { + ob->flag |= OB_DONE; + + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + GeometrySet geometry; + if (ob_eval->runtime.geometry_set_eval != nullptr) { + geometry = *ob_eval->runtime.geometry_set_eval; + } + + if (geometry.has_curves()) { + if (keep_original) { + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); + newob = basen->object; + + /* Decrement original curve's usage count. */ + Curve *legacy_curve = static_cast<Curve *>(newob->data); + id_us_min(&legacy_curve->id); + + /* Make a copy of the curve. */ + newob->data = BKE_id_copy(bmain, &legacy_curve->id); + } + else { + newob = ob; + } + + const CurveComponent &curve_component = *geometry.get_component_for_read<CurveComponent>(); + const Curves *curves_eval = curve_component.get_for_read(); + Curves *new_curves = static_cast<Curves *>(BKE_id_new(bmain, ID_CV, newob->id.name + 2)); + + newob->data = new_curves; + newob->type = OB_CURVES; + + blender::bke::CurvesGeometry::wrap( + new_curves->geometry) = blender::bke::CurvesGeometry::wrap(curves_eval->geometry); + BKE_object_material_from_eval_data(bmain, newob, &curves_eval->id); + + BKE_object_free_derived_caches(newob); + BKE_object_free_modifiers(newob, 0); + } + else { + BKE_reportf( + op->reports, RPT_WARNING, "Object '%s' has no evaluated curves data", ob->id.name + 2); + } + } else if (ob->type == OB_MESH && target == OB_POINTCLOUD) { ob->flag |= OB_DONE; @@ -3480,6 +3563,7 @@ void OBJECT_OT_convert(wmOperatorType *ot) /* properties */ ot->prop = RNA_def_enum( ot->srna, "target", convert_target_items, OB_MESH, "Target", "Type of object to convert to"); + RNA_def_enum_funcs(ot->prop, convert_target_items_fn); RNA_def_boolean(ot->srna, "keep_original", false, diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index a7379d7e492..114b2ce8102 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -446,9 +446,9 @@ static bool bake_object_check(ViewLayer *view_layer, } if (target == R_BAKE_TARGET_VERTEX_COLORS) { - MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); + const MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); const bool mcol_valid = (mcol != NULL); - MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR); + const MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR); if (mloopcol == NULL && !mcol_valid) { BKE_reportf(reports, RPT_ERROR, @@ -943,9 +943,9 @@ static bool bake_targets_init_vertex_colors(BakeTargets *targets, Object *ob, Re } Mesh *me = ob->data; - MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); + const MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); const bool mcol_valid = (mcol != NULL); - MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR); + const MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR); if (mloopcol == NULL && !mcol_valid) { BKE_report(reports, RPT_ERROR, "No vertex colors layer found to bake to"); return false; diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 2e878770347..d982d86fe77 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -1715,7 +1715,7 @@ static int constraint_copy_to_selected_exec(bContext *C, wmOperator *op) Object *prev_ob = NULL; - /* Copy all constraints from active posebone to all selected posebones. */ + /* Copy all constraints from active pose-bone to all selected pose-bones. */ CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) { /* If we're not handling the object we're copying from, copy all constraints over. */ if (pchan == chan) { @@ -2115,7 +2115,7 @@ static int pose_constraint_copy_exec(bContext *C, wmOperator *op) Object *prev_ob = NULL; - /* copy all constraints from active posebone to all selected posebones */ + /* Copy all constraints from active pose-bone to all selected pose-bones. */ CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) { /* if we're not handling the object we're copying from, copy all constraints over */ if (pchan != chan) { diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index cb0e76c11e4..c9626e674f2 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -17,6 +17,7 @@ #include "BLI_blenlib.h" #include "BLI_ghash.h" +#include "BLI_math_rotation.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -86,6 +87,7 @@ #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "RNA_types.h" #include "UI_interface_icons.h" @@ -1461,6 +1463,8 @@ void OBJECT_OT_paths_clear(wmOperatorType *ot) static int shade_smooth_exec(bContext *C, wmOperator *op) { const bool use_smooth = STREQ(op->idname, "OBJECT_OT_shade_smooth"); + const bool use_auto_smooth = RNA_boolean_get(op->ptr, "use_auto_smooth"); + const float auto_smooth_angle = RNA_float_get(op->ptr, "auto_smooth_angle"); bool changed_multi = false; bool has_linked_data = false; @@ -1508,6 +1512,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) bool changed = false; if (ob->type == OB_MESH) { BKE_mesh_smooth_flag_set(ob->data, use_smooth); + BKE_mesh_auto_smooth_flag_set(ob->data, use_auto_smooth, auto_smooth_angle); BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); changed = true; } @@ -1577,6 +1582,25 @@ void OBJECT_OT_shade_smooth(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + PropertyRNA *prop; + + prop = RNA_def_boolean( + ot->srna, + "use_auto_smooth", + false, + "Auto Smooth", + "Enable automatic smooth based on smooth/sharp faces/edges and angle between faces"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_property(ot->srna, "auto_smooth_angle", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_range(prop, 0.0f, DEG2RADF(180.0f)); + RNA_def_property_float_default(prop, DEG2RADF(30.0f)); + RNA_def_property_ui_text(prop, + "Angle", + "Maximum angle between face normals that will be considered as smooth" + "(unused if custom split normals data are available)"); } /** \} */ diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index cc6aa34d39d..fdf2cae026d 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -143,7 +143,12 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode) } break; case OB_CURVES: - if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT_CURVES)) { + if (U.experimental.use_new_curves_tools) { + if (mode & OB_MODE_EDIT) { + return true; + } + } + if (mode & OB_MODE_SCULPT_CURVES) { return true; } break; diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 76dcfbd8d36..a7efb45e803 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2258,49 +2258,6 @@ static bool make_override_library_object_overridable_check(Main *bmain, Object * return false; } -/* Set the object to override. */ -static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - Object *obact = ED_object_active_context(C); - - /* Sanity checks. */ - if (!scene || ID_IS_LINKED(scene) || !obact) { - return OPERATOR_CANCELLED; - } - - if ((!ID_IS_LINKED(obact) && obact->instance_collection != NULL && - ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection)) || - make_override_library_object_overridable_check(bmain, obact)) { - uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("OK?"), ICON_QUESTION); - uiLayout *layout = UI_popup_menu_layout(pup); - - /* Create operator menu item with relevant properties filled in. */ - PointerRNA opptr_dummy; - uiItemFullO_ptr( - layout, op->type, op->type->name, ICON_NONE, NULL, WM_OP_EXEC_REGION_WIN, 0, &opptr_dummy); - - /* Present the menu and be done... */ - UI_popup_menu_end(C, pup); - - /* This invoke just calls another instance of this operator... */ - return OPERATOR_INTERFACE; - } - - if (ID_IS_LINKED(obact)) { - /* Show menu with list of directly linked collections containing the active object. */ - WM_enum_search_invoke(C, op, event); - return OPERATOR_CANCELLED; - } - - /* Error.. cannot continue. */ - BKE_report(op->reports, - RPT_ERROR, - "Can only make library override for a referenced object or collection"); - return OPERATOR_CANCELLED; -} - static int make_override_library_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -2411,6 +2368,37 @@ static int make_override_library_exec(bContext *C, wmOperator *op) return success ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } +/* Set the object to override. */ +static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Object *obact = ED_object_active_context(C); + + /* Sanity checks. */ + if (!scene || ID_IS_LINKED(scene) || !obact) { + return OPERATOR_CANCELLED; + } + + if ((!ID_IS_LINKED(obact) && obact->instance_collection != NULL && + ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection)) || + make_override_library_object_overridable_check(bmain, obact)) { + return make_override_library_exec(C, op); + } + + if (ID_IS_LINKED(obact)) { + /* Show menu with list of directly linked collections containing the active object. */ + WM_enum_search_invoke(C, op, event); + return OPERATOR_CANCELLED; + } + + /* Error.. cannot continue. */ + BKE_report(op->reports, + RPT_ERROR, + "Can only make library override for a referenced object or collection"); + return OPERATOR_CANCELLED; +} + static bool make_override_library_poll(bContext *C) { Object *obact = CTX_data_active_object(C); diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc index a56f513e98f..addcedc4481 100644 --- a/source/blender/editors/render/render_preview.cc +++ b/source/blender/editors/render/render_preview.cc @@ -1306,7 +1306,7 @@ static ImBuf *icon_preview_imbuf_from_brush(Brush *brush) { static const int flags = IB_rect | IB_multilayer | IB_metadata; - char path[FILE_MAX]; + char filepath[FILE_MAX]; const char *folder; if (!(brush->icon_imbuf)) { @@ -1315,22 +1315,22 @@ static ImBuf *icon_preview_imbuf_from_brush(Brush *brush) if (brush->icon_filepath[0]) { /* First use the path directly to try and load the file. */ - BLI_strncpy(path, brush->icon_filepath, sizeof(brush->icon_filepath)); - BLI_path_abs(path, ID_BLEND_PATH_FROM_GLOBAL(&brush->id)); + BLI_strncpy(filepath, brush->icon_filepath, sizeof(brush->icon_filepath)); + BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&brush->id)); /* Use default color-spaces for brushes. */ - brush->icon_imbuf = IMB_loadiffname(path, flags, nullptr); + brush->icon_imbuf = IMB_loadiffname(filepath, flags, nullptr); /* otherwise lets try to find it in other directories */ if (!(brush->icon_imbuf)) { folder = BKE_appdir_folder_id(BLENDER_DATAFILES, "brushicons"); BLI_make_file_string( - BKE_main_blendfile_path_from_global(), path, folder, brush->icon_filepath); + BKE_main_blendfile_path_from_global(), filepath, folder, brush->icon_filepath); - if (path[0]) { + if (filepath[0]) { /* Use default color spaces. */ - brush->icon_imbuf = IMB_loadiffname(path, flags, nullptr); + brush->icon_imbuf = IMB_loadiffname(filepath, flags, nullptr); } } @@ -1821,13 +1821,13 @@ void PreviewLoadJob::run_fn(void *customdata, const char *deferred_data = static_cast<char *>(PRV_DEFERRED_DATA(preview)); const ThumbSource source = static_cast<ThumbSource>(deferred_data[0]); - const char *path = &deferred_data[1]; + const char *filepath = &deferred_data[1]; - // printf("loading deferred %d×%d preview for %s\n", request->sizex, request->sizey, path); + // printf("loading deferred %d×%d preview for %s\n", request->sizex, request->sizey, filepath); - IMB_thumb_path_lock(path); - ImBuf *thumb = IMB_thumb_manage(path, THB_LARGE, source); - IMB_thumb_path_unlock(path); + IMB_thumb_path_lock(filepath); + ImBuf *thumb = IMB_thumb_manage(filepath, THB_LARGE, source); + IMB_thumb_path_unlock(filepath); if (thumb) { /* PreviewImage assumes premultiplied alpha... */ diff --git a/source/blender/editors/scene/scene_edit.c b/source/blender/editors/scene/scene_edit.c index a13177942a8..57a9e6be917 100644 --- a/source/blender/editors/scene/scene_edit.c +++ b/source/blender/editors/scene/scene_edit.c @@ -67,7 +67,10 @@ static Scene *scene_add(Main *bmain, Scene *scene_old, eSceneCopyMethod method) } /** Add a new scene in the sequence editor. */ -static Scene *ED_scene_sequencer_add(Main *bmain, bContext *C, eSceneCopyMethod method) +Scene *ED_scene_sequencer_add(Main *bmain, + bContext *C, + eSceneCopyMethod method, + const bool assign_strip) { Sequence *seq = NULL; Scene *scene_active = CTX_data_scene(C); @@ -88,6 +91,11 @@ static Scene *ED_scene_sequencer_add(Main *bmain, bContext *C, eSceneCopyMethod Scene *scene_new = scene_add(bmain, scene_strip, method); + /* If don't need assign the scene to the strip, nothing else to do. */ + if (!assign_strip) { + return scene_new; + } + /* As the scene is created in sequencer, do not set the new scene as active. * This is useful for story-boarding where we want to keep actual scene active. * The new scene is linked to the active strip and the viewport updated. */ @@ -291,7 +299,7 @@ static int scene_new_sequencer_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); int type = RNA_enum_get(op->ptr, "type"); - if (ED_scene_sequencer_add(bmain, C, type) == NULL) { + if (ED_scene_sequencer_add(bmain, C, type, true) == NULL) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index d5385181055..43c19670a41 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1315,7 +1315,7 @@ void ED_screen_full_restore(bContext *C, ScrArea *area) else { ED_screen_state_toggle(C, win, area, state); } - /* warning: 'area' may be freed */ + /* WARNING: 'area' may be freed */ } /* otherwise just tile the area again */ else { diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 08eed52f440..d3bf28798c4 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -27,8 +27,8 @@ set(INC ) set(SRC - curves_sculpt_3d_brush.cc curves_sculpt_add.cc + curves_sculpt_brush.cc curves_sculpt_comb.cc curves_sculpt_delete.cc curves_sculpt_grow_shrink.cc diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index 0d399419ad8..04ae6c62aee 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -18,9 +18,11 @@ #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" +#include "BKE_curves_utils.hh" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_paint.h" +#include "BKE_report.h" #include "BKE_spline.hh" #include "DNA_brush_enums.h" @@ -35,6 +37,8 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "WM_api.h" + /** * The code below uses a prefix naming convention to indicate the coordinate space: * cu: Local space of the curves object that is being edited. @@ -62,7 +66,7 @@ class AddOperation : public CurvesSculptStrokeOperation { } } - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; static void initialize_straight_curve_positions(const float3 &p1, @@ -81,11 +85,12 @@ static void initialize_straight_curve_positions(const float3 &p1, */ struct AddOperationExecutor { AddOperation *self_ = nullptr; - Depsgraph *depsgraph_ = nullptr; - Scene *scene_ = nullptr; - Object *object_ = nullptr; + const Depsgraph *depsgraph_ = nullptr; + const Scene *scene_ = nullptr; ARegion *region_ = nullptr; - View3D *v3d_ = nullptr; + const View3D *v3d_ = nullptr; + + Object *object_ = nullptr; Curves *curves_id_ = nullptr; CurvesGeometry *curves_ = nullptr; @@ -94,9 +99,9 @@ struct AddOperationExecutor { Span<MLoopTri> surface_looptris_; Span<float3> corner_normals_su_; - CurvesSculpt *curves_sculpt_ = nullptr; - Brush *brush_ = nullptr; - BrushCurvesSculptSettings *brush_settings_ = nullptr; + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + const BrushCurvesSculptSettings *brush_settings_ = nullptr; float brush_radius_re_; float2 brush_pos_re_; @@ -104,10 +109,11 @@ struct AddOperationExecutor { bool use_front_face_; bool interpolate_length_; bool interpolate_shape_; + bool interpolate_point_count_; bool use_interpolation_; float new_curve_length_; int add_amount_; - int points_per_curve_ = 8; + int constant_points_per_curve_; /** Various matrices to convert between coordinate spaces. */ float4x4 curves_to_world_mat_; @@ -128,14 +134,23 @@ struct AddOperationExecutor { Vector<int> looptri_indices; }; - void execute(AddOperation &self, bContext *C, const StrokeExtension &stroke_extension) + struct NeighborInfo { + /* Curve index of the neighbor. */ + int index; + /* The weights of all neighbors of a new curve add up to 1. */ + float weight; + }; + static constexpr int max_neighbors = 5; + using NeighborsVector = Vector<NeighborInfo, max_neighbors>; + + void execute(AddOperation &self, const bContext &C, const StrokeExtension &stroke_extension) { self_ = &self; - depsgraph_ = CTX_data_depsgraph_pointer(C); - scene_ = CTX_data_scene(C); - object_ = CTX_data_active_object(C); - region_ = CTX_wm_region(C); - v3d_ = CTX_wm_view3d(C); + depsgraph_ = CTX_data_depsgraph_pointer(&C); + scene_ = CTX_data_scene(&C); + object_ = CTX_data_active_object(&C); + region_ = CTX_wm_region(&C); + v3d_ = CTX_wm_view3d(&C); curves_id_ = static_cast<Curves *>(object_->data); curves_ = &CurvesGeometry::wrap(curves_id_->geometry); @@ -162,7 +177,7 @@ struct AddOperationExecutor { surface_->totloop}; curves_sculpt_ = scene_->toolsettings->curves_sculpt; - brush_ = BKE_paint_brush(&curves_sculpt_->paint); + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); brush_settings_ = brush_->curves_sculpt_settings; brush_radius_re_ = BKE_brush_size_get(scene_, brush_); brush_pos_re_ = stroke_extension.mouse_position; @@ -171,9 +186,12 @@ struct AddOperationExecutor { const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( brush_->falloff_shape); add_amount_ = std::max(0, brush_settings_->add_amount); + constant_points_per_curve_ = std::max(2, brush_settings_->points_per_curve); interpolate_length_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; interpolate_shape_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE; - use_interpolation_ = interpolate_length_ || interpolate_shape_; + interpolate_point_count_ = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT; + use_interpolation_ = interpolate_length_ || interpolate_shape_ || interpolate_point_count_; new_curve_length_ = brush_settings_->curve_length; tot_old_curves_ = curves_->curves_num(); @@ -183,7 +201,9 @@ struct AddOperationExecutor { return; } - RandomNumberGenerator rng{(uint32_t)(PIL_check_seconds_timer() * 1000000.0f)}; + const double time = PIL_check_seconds_timer() * 1000000.0; + /* Use a pointer cast to avoid overflow warnings. */ + RandomNumberGenerator rng{*(uint32_t *)(&time)}; BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); }); @@ -194,13 +214,13 @@ struct AddOperationExecutor { /* Sample points on the surface using one of multiple strategies. */ AddedPoints added_points; if (add_amount_ == 1) { - this->sample_in_center(added_points); + this->sample_in_center_with_symmetry(added_points); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - this->sample_projected(rng, added_points); + this->sample_projected_with_symmetry(rng, added_points); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - this->sample_spherical(rng, added_points); + this->sample_spherical_with_symmetry(rng, added_points); } else { BLI_assert_unreachable(); @@ -211,20 +231,31 @@ struct AddOperationExecutor { return; } + Array<NeighborsVector> neighbors_per_curve; if (use_interpolation_) { this->ensure_curve_roots_kdtree(); + neighbors_per_curve = this->find_curve_neighbors(added_points); } + /* Resize to add the new curves, building the offsets in the array owned by the curves. */ const int tot_added_curves = added_points.bary_coords.size(); - const int tot_added_points = tot_added_curves * points_per_curve_; + curves_->resize(curves_->points_num(), curves_->curves_num() + tot_added_curves); + if (interpolate_point_count_) { + this->initialize_curve_offsets_with_interpolation(neighbors_per_curve); + } + else { + this->initialize_curve_offsets_without_interpolation(constant_points_per_curve_); + } + + /* Resize to add the correct point count calculated as part of building the offsets. */ + curves_->resize(curves_->offsets().last(), curves_->curves_num()); - curves_->resize(curves_->points_num() + tot_added_points, - curves_->curves_num() + tot_added_curves); + this->initialize_attributes(added_points, neighbors_per_curve); - threading::parallel_invoke([&]() { this->initialize_curve_offsets(tot_added_curves); }, - [&]() { this->initialize_attributes(added_points); }); + curves_->update_curve_types(); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); ED_region_tag_redraw(region_); } @@ -241,13 +272,27 @@ struct AddOperationExecutor { /** * Sample a single point exactly at the mouse position. */ - void sample_in_center(AddedPoints &r_added_points) + void sample_in_center_with_symmetry(AddedPoints &r_added_points) { float3 ray_start_wo, ray_end_wo; ED_view3d_win_to_segment_clipped( depsgraph_, region_, v3d_, brush_pos_re_, ray_start_wo, ray_end_wo, true); const float3 ray_start_su = world_to_surface_mat_ * ray_start_wo; const float3 ray_end_su = world_to_surface_mat_ * ray_end_wo; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->sample_in_center( + r_added_points, brush_transform * ray_start_su, brush_transform * ray_end_su); + } + } + + void sample_in_center(AddedPoints &r_added_points, + const float3 &ray_start_su, + const float3 &ray_end_su) + { const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); BVHTreeRayHit ray_hit; @@ -280,11 +325,23 @@ struct AddOperationExecutor { /** * Sample points by shooting rays within the brush radius in the 3D view. */ - void sample_projected(RandomNumberGenerator &rng, AddedPoints &r_added_points) + void sample_projected_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points) { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->sample_projected(rng, r_added_points, brush_transform); + } + } + + void sample_projected(RandomNumberGenerator &rng, + AddedPoints &r_added_points, + const float4x4 &brush_transform) + { + const int old_amount = r_added_points.bary_coords.size(); const int max_iterations = std::max(100'000, add_amount_ * 10); int current_iteration = 0; - while (r_added_points.bary_coords.size() < add_amount_) { + while (r_added_points.bary_coords.size() < old_amount + add_amount_) { if (current_iteration++ >= max_iterations) { break; } @@ -296,8 +353,8 @@ struct AddOperationExecutor { float3 ray_start_wo, ray_end_wo; ED_view3d_win_to_segment_clipped( depsgraph_, region_, v3d_, pos_re, ray_start_wo, ray_end_wo, true); - const float3 ray_start_su = world_to_surface_mat_ * ray_start_wo; - const float3 ray_end_su = world_to_surface_mat_ * ray_end_wo; + const float3 ray_start_su = brush_transform * (world_to_surface_mat_ * ray_start_wo); + const float3 ray_end_su = brush_transform * (world_to_surface_mat_ * ray_end_wo); const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); BVHTreeRayHit ray_hit; @@ -339,7 +396,7 @@ struct AddOperationExecutor { /** * Sample points in a 3D sphere around the surface position that the mouse hovers over. */ - void sample_spherical(RandomNumberGenerator &rng, AddedPoints &r_added_points) + void sample_spherical_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points) { /* Find ray that starts in the center of the brush. */ float3 brush_ray_start_wo, brush_ray_end_wo; @@ -347,7 +404,6 @@ struct AddOperationExecutor { depsgraph_, region_, v3d_, brush_pos_re_, brush_ray_start_wo, brush_ray_end_wo, true); const float3 brush_ray_start_su = world_to_surface_mat_ * brush_ray_start_wo; const float3 brush_ray_end_su = world_to_surface_mat_ * brush_ray_end_wo; - const float3 brush_ray_direction_su = math::normalize(brush_ray_end_su - brush_ray_start_su); /* Find ray that starts on the boundary of the brush. That is used to compute the brush radius * in 3D. */ @@ -362,6 +418,27 @@ struct AddOperationExecutor { const float3 brush_radius_ray_start_su = world_to_surface_mat_ * brush_radius_ray_start_wo; const float3 brush_radius_ray_end_su = world_to_surface_mat_ * brush_radius_ray_end_wo; + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->sample_spherical(rng, + r_added_points, + brush_transform * brush_ray_start_su, + brush_transform * brush_ray_end_su, + brush_transform * brush_radius_ray_start_su, + brush_transform * brush_radius_ray_end_su); + } + } + + void sample_spherical(RandomNumberGenerator &rng, + AddedPoints &r_added_points, + const float3 &brush_ray_start_su, + const float3 &brush_ray_end_su, + const float3 &brush_radius_ray_start_su, + const float3 &brush_radius_ray_end_su) + { + const float3 brush_ray_direction_su = math::normalize(brush_ray_end_su - brush_ray_start_su); + BVHTreeRayHit ray_hit; ray_hit.dist = FLT_MAX; ray_hit.index = -1; @@ -426,7 +503,8 @@ struct AddOperationExecutor { const int max_iterations = 5; int current_iteration = 0; - while (r_added_points.bary_coords.size() < add_amount_) { + const int old_amount = r_added_points.bary_coords.size(); + while (r_added_points.bary_coords.size() < old_amount + add_amount_) { if (current_iteration++ >= max_iterations) { break; } @@ -506,8 +584,8 @@ struct AddOperationExecutor { } /* Remove samples when there are too many. */ - while (r_added_points.bary_coords.size() > add_amount_) { - const int index_to_remove = rng.get_int32(r_added_points.bary_coords.size()); + while (r_added_points.bary_coords.size() > old_amount + add_amount_) { + const int index_to_remove = rng.get_int32(add_amount_) + old_amount; r_added_points.bary_coords.remove_and_reorder(index_to_remove); r_added_points.looptri_indices.remove_and_reorder(index_to_remove); r_added_points.positions_cu.remove_and_reorder(index_to_remove); @@ -527,33 +605,42 @@ struct AddOperationExecutor { } } - void initialize_curve_offsets(const int tot_added_curves) + void initialize_curve_offsets_with_interpolation(const Span<NeighborsVector> neighbors_per_curve) { - MutableSpan<int> offsets = curves_->offsets_for_write(); - threading::parallel_for(IndexRange(tot_added_curves), 1024, [&](const IndexRange range) { - for (const int i : range) { - const int curve_i = tot_old_curves_ + i; - offsets[curve_i + 1] = tot_old_points_ + (i + 1) * points_per_curve_; + MutableSpan<int> new_offsets = curves_->offsets_for_write().drop_front(tot_old_curves_); + + attribute_math::DefaultMixer<int> mixer{new_offsets}; + threading::parallel_for(neighbors_per_curve.index_range(), 1024, [&](IndexRange curves_range) { + for (const int i : curves_range) { + if (neighbors_per_curve[i].is_empty()) { + mixer.mix_in(i, constant_points_per_curve_, 1.0f); + } + else { + for (const NeighborInfo &neighbor : neighbors_per_curve[i]) { + const int neighbor_points_num = curves_->points_for_curve(neighbor.index).size(); + mixer.mix_in(i, neighbor_points_num, neighbor.weight); + } + } } }); - } + mixer.finalize(); - struct NeighborInfo { - /* Curve index of the neighbor. */ - int index; - /* The weights of all neighbors of a new curve add up to 1. */ - float weight; - }; - static constexpr int max_neighbors = 5; - using NeighborsVector = Vector<NeighborInfo, max_neighbors>; + bke::curves::accumulate_counts_to_offsets(new_offsets, tot_old_points_); + } - void initialize_attributes(const AddedPoints &added_points) + void initialize_curve_offsets_without_interpolation(const int points_per_curve) { - Array<NeighborsVector> neighbors_per_curve; - if (use_interpolation_) { - neighbors_per_curve = this->find_curve_neighbors(added_points); + MutableSpan<int> new_offsets = curves_->offsets_for_write().drop_front(tot_old_curves_); + int offset = tot_old_points_; + for (const int i : new_offsets.index_range()) { + new_offsets[i] = offset; + offset += points_per_curve; } + } + void initialize_attributes(const AddedPoints &added_points, + const Span<NeighborsVector> neighbors_per_curve) + { Array<float> new_lengths_cu(added_points.bary_coords.size()); if (interpolate_length_) { this->interpolate_lengths(neighbors_per_curve, new_lengths_cu); @@ -682,15 +769,14 @@ struct AddOperationExecutor { threading::parallel_for( added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { for (const int i : range) { - const int first_point_i = tot_old_points_ + i * points_per_curve_; + const IndexRange points = curves_->points_for_curve(tot_old_curves_ + i); const float3 &root_cu = added_points.positions_cu[i]; const float length = lengths_cu[i]; const float3 &normal_su = normals_su[i]; const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); const float3 tip_cu = root_cu + length * normal_cu; - initialize_straight_curve_positions( - root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); + initialize_straight_curve_positions(root_cu, tip_cu, positions_cu.slice(points)); } }); } @@ -711,23 +797,22 @@ struct AddOperationExecutor { added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { for (const int i : range) { const Span<NeighborInfo> neighbors = neighbors_per_curve[i]; + const IndexRange points = curves_->points_for_curve(tot_old_curves_ + i); const float length_cu = new_lengths_cu[i]; const float3 &normal_su = new_normals_su[i]; const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); const float3 &root_cu = added_points.positions_cu[i]; - const int first_point_i = tot_old_points_ + i * points_per_curve_; if (neighbors.is_empty()) { /* If there are no neighbors, just make a straight line. */ const float3 tip_cu = root_cu + length_cu * normal_cu; - initialize_straight_curve_positions( - root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); + initialize_straight_curve_positions(root_cu, tip_cu, positions_cu.slice(points)); continue; } - positions_cu.slice(first_point_i, points_per_curve_).fill(root_cu); + positions_cu.slice(points).fill(root_cu); for (const NeighborInfo &neighbor : neighbors) { const int neighbor_curve_i = neighbor.index; @@ -761,8 +846,8 @@ struct AddOperationExecutor { const float neighbor_length_cu = neighbor_spline.length(); const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu); - const float resample_factor = (1.0f / (points_per_curve_ - 1.0f)) * length_factor; - for (const int j : IndexRange(points_per_curve_)) { + const float resample_factor = (1.0f / (points.size() - 1.0f)) * length_factor; + for (const int j : IndexRange(points.size())) { const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor( j * resample_factor); const float index_factor = lookup.evaluated_index + lookup.factor; @@ -772,7 +857,7 @@ struct AddOperationExecutor { const float3 relative_coord = p - neighbor_root_cu; float3 rotated_relative_coord = relative_coord; mul_m3_v3(normal_rotation_cu, rotated_relative_coord); - positions_cu[first_point_i + j] += neighbor.weight * rotated_relative_coord; + positions_cu[points[j]] += neighbor.weight * rotated_relative_coord; } } } @@ -780,14 +865,23 @@ struct AddOperationExecutor { } }; -void AddOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) +void AddOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) { AddOperationExecutor executor; executor.execute(*this, C, stroke_extension); } -std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation() +std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C, + ReportList *reports) { + const Object &ob_active = *CTX_data_active_object(&C); + BLI_assert(ob_active.type == OB_CURVES); + const Curves &curves_id = *static_cast<Curves *>(ob_active.data); + if (curves_id.surface == nullptr || curves_id.surface->type != OB_MESH) { + BKE_report(reports, RPT_WARNING, "Can not use Add brush when there is no surface mesh"); + return {}; + } + return std::make_unique<AddOperation>(); } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc index 945bb09c0c6..9ebbf72806e 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc @@ -41,9 +41,9 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu const float3 &ray_start_cu, const float3 &ray_end_cu, const float brush_radius_re, - ARegion ®ion, - RegionView3D &rv3d, - Object &object) + const ARegion ®ion, + const RegionView3D &rv3d, + const Object &object) { /* This value might have to be adjusted based on user feedback. */ const float brush_inner_radius_re = std::min<float>(brush_radius_re, (float)UI_UNIT_X / 3.0f); @@ -116,8 +116,11 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu const float distance_sq_re = math::distance_squared(brush_pos_re, closest_re); + float3 brush_position_cu; + closest_to_line_segment_v3(brush_position_cu, closest_cu, ray_start_cu, ray_end_cu); + BrushPositionCandidate candidate; - candidate.position_cu = closest_cu; + candidate.position_cu = brush_position_cu; candidate.depth_sq_cu = depth_sq_cu; candidate.distance_sq_re = distance_sq_re; @@ -138,23 +141,21 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu return best_candidate.position_cu; } -std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, - Object &curves_object, +std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, + const ARegion ®ion, + const View3D &v3d, + const RegionView3D &rv3d, + const Object &curves_object, const float2 &brush_pos_re, const float brush_radius_re) { - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); - ARegion *region = CTX_wm_region(&C); - View3D *v3d = CTX_wm_view3d(&C); - RegionView3D *rv3d = CTX_wm_region_view3d(&C); - - Curves &curves_id = *static_cast<Curves *>(curves_object.data); - CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - Object *surface_object = curves_id.surface; + const Curves &curves_id = *static_cast<Curves *>(curves_object.data); + const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + const Object *surface_object = curves_id.surface; float3 center_ray_start_wo, center_ray_end_wo; ED_view3d_win_to_segment_clipped( - depsgraph, region, v3d, brush_pos_re, center_ray_start_wo, center_ray_end_wo, true); + &depsgraph, ®ion, &v3d, brush_pos_re, center_ray_start_wo, center_ray_end_wo, true); /* Shorten ray when the surface object is hit. */ if (surface_object != nullptr) { @@ -202,8 +203,8 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, center_ray_start_cu, center_ray_end_cu, brush_radius_re, - *region, - *rv3d, + region, + rv3d, curves_object); if (!brush_position_optional_cu.has_value()) { /* Nothing found. */ @@ -213,9 +214,9 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, /* Determine the 3D brush radius. */ float3 radius_ray_start_wo, radius_ray_end_wo; - ED_view3d_win_to_segment_clipped(depsgraph, - region, - v3d, + ED_view3d_win_to_segment_clipped(&depsgraph, + ®ion, + &v3d, brush_pos_re + float2(brush_radius_re, 0.0f), radius_ray_start_wo, radius_ray_end_wo, @@ -229,4 +230,32 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, return brush_3d; } +Vector<float4x4> get_symmetry_brush_transforms(const eCurvesSymmetryType symmetry) +{ + Vector<float4x4> matrices; + + auto symmetry_to_factors = [&](const eCurvesSymmetryType type) -> Span<float> { + if (symmetry & type) { + static std::array<float, 2> values = {1.0f, -1.0f}; + return values; + } + static std::array<float, 1> values = {1.0f}; + return values; + }; + + for (const float x : symmetry_to_factors(CURVES_SYMMETRY_X)) { + for (const float y : symmetry_to_factors(CURVES_SYMMETRY_Y)) { + for (const float z : symmetry_to_factors(CURVES_SYMMETRY_Z)) { + float4x4 matrix = float4x4::identity(); + matrix.values[0][0] = x; + matrix.values[1][1] = y; + matrix.values[2][2] = z; + matrices.append(matrix); + } + } + } + + return matrices; +} + } // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc index 232d632aa3f..72257659c52 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc @@ -37,6 +37,8 @@ #include "UI_interface.h" +#include "WM_api.h" + /** * The code below uses a prefix naming convention to indicate the coordinate space: * cu: Local space of the curves object that is being edited. @@ -67,7 +69,7 @@ class CombOperation : public CurvesSculptStrokeOperation { friend struct CombOperationExecutor; public: - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; /** @@ -76,21 +78,20 @@ class CombOperation : public CurvesSculptStrokeOperation { */ struct CombOperationExecutor { CombOperation *self_ = nullptr; - bContext *C_ = nullptr; - Depsgraph *depsgraph_ = nullptr; - Scene *scene_ = nullptr; - Object *object_ = nullptr; + const Depsgraph *depsgraph_ = nullptr; + const Scene *scene_ = nullptr; ARegion *region_ = nullptr; - View3D *v3d_ = nullptr; - RegionView3D *rv3d_ = nullptr; + const View3D *v3d_ = nullptr; + const RegionView3D *rv3d_ = nullptr; - CurvesSculpt *curves_sculpt_ = nullptr; - Brush *brush_ = nullptr; + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; float brush_radius_re_; float brush_strength_; eBrushFalloffShape falloff_shape_; + Object *object_ = nullptr; Curves *curves_id_ = nullptr; CurvesGeometry *curves_ = nullptr; @@ -110,22 +111,21 @@ struct CombOperationExecutor { BVHTreeFromMesh surface_bvh_; - void execute(CombOperation &self, bContext *C, const StrokeExtension &stroke_extension) + void execute(CombOperation &self, const bContext &C, const StrokeExtension &stroke_extension) { self_ = &self; BLI_SCOPED_DEFER([&]() { self_->brush_pos_last_re_ = stroke_extension.mouse_position; }); - C_ = C; - depsgraph_ = CTX_data_depsgraph_pointer(C); - scene_ = CTX_data_scene(C); - object_ = CTX_data_active_object(C); - region_ = CTX_wm_region(C); - v3d_ = CTX_wm_view3d(C); - rv3d_ = CTX_wm_region_view3d(C); + depsgraph_ = CTX_data_depsgraph_pointer(&C); + scene_ = CTX_data_scene(&C); + object_ = CTX_data_active_object(&C); + region_ = CTX_wm_region(&C); + v3d_ = CTX_wm_view3d(&C); + rv3d_ = CTX_wm_region_view3d(&C); curves_sculpt_ = scene_->toolsettings->curves_sculpt; - brush_ = BKE_paint_brush(&curves_sculpt_->paint); + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); brush_radius_re_ = BKE_brush_size_get(scene_, brush_); brush_strength_ = BKE_brush_alpha_get(scene_, brush_); @@ -173,10 +173,10 @@ struct CombOperationExecutor { EnumerableThreadSpecific<Vector<int>> changed_curves; if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) { - this->comb_projected(changed_curves); + this->comb_projected_with_symmetry(changed_curves); } else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { - this->comb_spherical(changed_curves); + this->comb_spherical_with_symmetry(changed_curves); } else { BLI_assert_unreachable(); @@ -186,14 +186,27 @@ struct CombOperationExecutor { curves_->tag_positions_changed(); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); ED_region_tag_redraw(region_); } /** * Do combing in screen space. */ - void comb_projected(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) + void comb_projected_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->comb_projected(r_changed_curves, brush_transform); + } + } + + void comb_projected(EnumerableThreadSpecific<Vector<int>> &r_changed_curves, + const float4x4 &brush_transform) { + const float4x4 brush_transform_inv = brush_transform.inverted(); + MutableSpan<float3> positions_cu = curves_->positions_for_write(); float4x4 projection; @@ -207,7 +220,7 @@ struct CombOperationExecutor { bool curve_changed = false; const IndexRange points = curves_->points_for_curve(curve_i); for (const int point_i : points.drop_front(1)) { - const float3 old_pos_cu = positions_cu[point_i]; + const float3 old_pos_cu = brush_transform_inv * positions_cu[point_i]; /* Find the position of the point in screen space. */ float2 old_pos_re; @@ -232,7 +245,8 @@ struct CombOperationExecutor { float3 new_position_wo; ED_view3d_win_to_3d( v3d_, region_, curves_to_world_mat_ * old_pos_cu, new_position_re, new_position_wo); - const float3 new_position_cu = world_to_curves_mat_ * new_position_wo; + const float3 new_position_cu = brush_transform * + (world_to_curves_mat_ * new_position_wo); positions_cu[point_i] = new_position_cu; curve_changed = true; @@ -247,10 +261,8 @@ struct CombOperationExecutor { /** * Do combing in 3D space. */ - void comb_spherical(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) + void comb_spherical_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) { - MutableSpan<float3> positions_cu = curves_->positions_for_write(); - float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); @@ -268,10 +280,26 @@ struct CombOperationExecutor { const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; - const float3 brush_diff_cu = brush_end_cu - brush_start_cu; - const float brush_radius_cu = self_->brush_3d_.radius_cu; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->comb_spherical(r_changed_curves, + brush_transform * brush_start_cu, + brush_transform * brush_end_cu, + brush_radius_cu); + } + } + + void comb_spherical(EnumerableThreadSpecific<Vector<int>> &r_changed_curves, + const float3 &brush_start_cu, + const float3 &brush_end_cu, + const float brush_radius_cu) + { + MutableSpan<float3> positions_cu = curves_->positions_for_write(); const float brush_radius_sq_cu = pow2f(brush_radius_cu); + const float3 brush_diff_cu = brush_end_cu - brush_start_cu; threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { Vector<int> &local_changed_curves = r_changed_curves.local(); @@ -314,7 +342,7 @@ struct CombOperationExecutor { void initialize_spherical_brush_reference_point() { std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( - *C_, *object_, brush_pos_re_, brush_radius_re_); + *depsgraph_, *region_, *v3d_, *rv3d_, *object_, brush_pos_re_, brush_radius_re_); if (brush_3d.has_value()) { self_->brush_3d_ = *brush_3d; } @@ -366,7 +394,7 @@ struct CombOperationExecutor { } }; -void CombOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) +void CombOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) { CombOperationExecutor executor; executor.execute(*this, C, stroke_extension); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc index 2841a19d677..f07c0a65933 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc @@ -35,6 +35,8 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "WM_api.h" + /** * The code below uses a suffix naming convention to indicate the coordinate space: * cu: Local space of the curves object that is being edited. @@ -55,24 +57,23 @@ class DeleteOperation : public CurvesSculptStrokeOperation { friend struct DeleteOperationExecutor; public: - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; struct DeleteOperationExecutor { DeleteOperation *self_ = nullptr; - bContext *C_ = nullptr; - Depsgraph *depsgraph_ = nullptr; - Scene *scene_ = nullptr; - Object *object_ = nullptr; + const Depsgraph *depsgraph_ = nullptr; + const Scene *scene_ = nullptr; ARegion *region_ = nullptr; - View3D *v3d_ = nullptr; - RegionView3D *rv3d_ = nullptr; + const View3D *v3d_ = nullptr; + const RegionView3D *rv3d_ = nullptr; + Object *object_ = nullptr; Curves *curves_id_ = nullptr; CurvesGeometry *curves_ = nullptr; - CurvesSculpt *curves_sculpt_ = nullptr; - Brush *brush_ = nullptr; + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; float brush_radius_re_; float2 brush_pos_re_; @@ -81,24 +82,23 @@ struct DeleteOperationExecutor { float4x4 curves_to_world_mat_; float4x4 world_to_curves_mat_; - void execute(DeleteOperation &self, bContext *C, const StrokeExtension &stroke_extension) + void execute(DeleteOperation &self, const bContext &C, const StrokeExtension &stroke_extension) { BLI_SCOPED_DEFER([&]() { self.brush_pos_prev_re_ = stroke_extension.mouse_position; }); self_ = &self; - C_ = C; - depsgraph_ = CTX_data_depsgraph_pointer(C); - scene_ = CTX_data_scene(C); - object_ = CTX_data_active_object(C); - region_ = CTX_wm_region(C); - v3d_ = CTX_wm_view3d(C); - rv3d_ = CTX_wm_region_view3d(C); + depsgraph_ = CTX_data_depsgraph_pointer(&C); + scene_ = CTX_data_scene(&C); + object_ = CTX_data_active_object(&C); + region_ = CTX_wm_region(&C); + v3d_ = CTX_wm_view3d(&C); + rv3d_ = CTX_wm_region_view3d(&C); curves_id_ = static_cast<Curves *>(object_->data); curves_ = &CurvesGeometry::wrap(curves_id_->geometry); curves_sculpt_ = scene_->toolsettings->curves_sculpt; - brush_ = BKE_paint_brush(&curves_sculpt_->paint); + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); brush_radius_re_ = BKE_brush_size_get(scene_, brush_); brush_pos_re_ = stroke_extension.mouse_position; @@ -117,56 +117,71 @@ struct DeleteOperationExecutor { } } + Array<bool> curves_to_delete(curves_->curves_num(), false); if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - this->delete_projected(); + this->delete_projected_with_symmetry(curves_to_delete); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - this->delete_spherical(); + this->delete_spherical_with_symmetry(curves_to_delete); } else { BLI_assert_unreachable(); } + Vector<int64_t> indices; + const IndexMask mask = index_mask_ops::find_indices_based_on_predicate( + curves_->curves_range(), 4096, indices, [&](const int curve_i) { + return curves_to_delete[curve_i]; + }); + + curves_->remove_curves(mask); + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); ED_region_tag_redraw(region_); } - void delete_projected() + void delete_projected_with_symmetry(MutableSpan<bool> curves_to_delete) { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->delete_projected(brush_transform, curves_to_delete); + } + } + + void delete_projected(const float4x4 &brush_transform, MutableSpan<bool> curves_to_delete) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); + float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); Span<float3> positions_cu = curves_->positions(); - /* Find indices of curves that have to be removed. */ - Vector<int64_t> indices; - const IndexMask curves_to_remove = index_mask_ops::find_indices_based_on_predicate( - curves_->curves_range(), 512, indices, [&](const int curve_i) { - const IndexRange point_range = curves_->points_for_curve(curve_i); - for (const int segment_i : IndexRange(point_range.size() - 1)) { - const float3 pos1_cu = positions_cu[point_range[segment_i]]; - const float3 pos2_cu = positions_cu[point_range[segment_i + 1]]; - - float2 pos1_re, pos2_re; - ED_view3d_project_float_v2_m4(region_, pos1_cu, pos1_re, projection.values); - ED_view3d_project_float_v2_m4(region_, pos2_cu, pos2_re, projection.values); - - const float dist = dist_seg_seg_v2( - pos1_re, pos2_re, brush_pos_prev_re_, brush_pos_re_); - if (dist <= brush_radius_re_) { - return true; - } + threading::parallel_for(curves_->curves_range(), 512, [&](IndexRange curve_range) { + for (const int curve_i : curve_range) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int segment_i : IndexRange(points.size() - 1)) { + const float3 pos1_cu = brush_transform_inv * positions_cu[points[segment_i]]; + const float3 pos2_cu = brush_transform_inv * positions_cu[points[segment_i + 1]]; + + float2 pos1_re, pos2_re; + ED_view3d_project_float_v2_m4(region_, pos1_cu, pos1_re, projection.values); + ED_view3d_project_float_v2_m4(region_, pos2_cu, pos2_re, projection.values); + + const float dist = dist_seg_seg_v2(pos1_re, pos2_re, brush_pos_prev_re_, brush_pos_re_); + if (dist <= brush_radius_re_) { + curves_to_delete[curve_i] = true; + break; } - return false; - }); - - curves_->remove_curves(curves_to_remove); + } + } + }); } - void delete_spherical() + void delete_spherical_with_symmetry(MutableSpan<bool> curves_to_delete) { - Span<float3> positions_cu = curves_->positions(); - float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); @@ -184,48 +199,62 @@ struct DeleteOperationExecutor { const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->delete_spherical( + brush_transform * brush_start_cu, brush_transform * brush_end_cu, curves_to_delete); + } + } + + void delete_spherical(const float3 &brush_start_cu, + const float3 &brush_end_cu, + MutableSpan<bool> curves_to_delete) + { + Span<float3> positions_cu = curves_->positions(); + const float brush_radius_cu = self_->brush_3d_.radius_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); - Vector<int64_t> indices; - const IndexMask curves_to_remove = index_mask_ops::find_indices_based_on_predicate( - curves_->curves_range(), 512, indices, [&](const int curve_i) { - const IndexRange points = curves_->points_for_curve(curve_i); - for (const int segment_i : IndexRange(points.size() - 1)) { - const float3 pos1_cu = positions_cu[points[segment_i]]; - const float3 pos2_cu = positions_cu[points[segment_i] + 1]; - - float3 closest_segment_cu, closest_brush_cu; - isect_seg_seg_v3(pos1_cu, - pos2_cu, - brush_start_cu, - brush_end_cu, - closest_segment_cu, - closest_brush_cu); - const float distance_to_brush_sq_cu = math::distance_squared(closest_segment_cu, - closest_brush_cu); - if (distance_to_brush_sq_cu > brush_radius_sq_cu) { - continue; - } - return true; + threading::parallel_for(curves_->curves_range(), 512, [&](IndexRange curve_range) { + for (const int curve_i : curve_range) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int segment_i : IndexRange(points.size() - 1)) { + const float3 pos1_cu = positions_cu[points[segment_i]]; + const float3 pos2_cu = positions_cu[points[segment_i] + 1]; + + float3 closest_segment_cu, closest_brush_cu; + isect_seg_seg_v3(pos1_cu, + pos2_cu, + brush_start_cu, + brush_end_cu, + closest_segment_cu, + closest_brush_cu); + const float distance_to_brush_sq_cu = math::distance_squared(closest_segment_cu, + closest_brush_cu); + if (distance_to_brush_sq_cu > brush_radius_sq_cu) { + continue; } - return false; - }); - - curves_->remove_curves(curves_to_remove); + curves_to_delete[curve_i] = true; + break; + } + } + }); } void initialize_spherical_brush_reference_point() { std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( - *C_, *object_, brush_pos_re_, brush_radius_re_); + *depsgraph_, *region_, *v3d_, *rv3d_, *object_, brush_pos_re_, brush_radius_re_); if (brush_3d.has_value()) { self_->brush_3d_ = *brush_3d; } } }; -void DeleteOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) +void DeleteOperation::on_stroke_extended(const bContext &C, + const StrokeExtension &stroke_extension) { DeleteOperationExecutor executor; executor.execute(*this, C, stroke_extension); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc index 6228a643a76..d10cf239dd2 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc @@ -36,6 +36,8 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "WM_api.h" + #include "curves_sculpt_intern.hh" /** @@ -68,10 +70,10 @@ class CurvesEffect { */ class ShrinkCurvesEffect : public CurvesEffect { private: - Brush &brush_; + const Brush &brush_; public: - ShrinkCurvesEffect(Brush &brush) : brush_(brush) + ShrinkCurvesEffect(const Brush &brush) : brush_(brush) { } @@ -203,10 +205,10 @@ class ExtrapolateCurvesEffect : public CurvesEffect { class ScaleCurvesEffect : public CurvesEffect { private: bool scale_up_; - Brush &brush_; + const Brush &brush_; public: - ScaleCurvesEffect(bool scale_up, Brush &brush) : scale_up_(scale_up), brush_(brush) + ScaleCurvesEffect(bool scale_up, const Brush &brush) : scale_up_(scale_up), brush_(brush) { } @@ -261,7 +263,7 @@ class CurvesEffectOperation : public CurvesSculptStrokeOperation { { } - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; /** @@ -270,17 +272,17 @@ class CurvesEffectOperation : public CurvesSculptStrokeOperation { */ struct CurvesEffectOperationExecutor { CurvesEffectOperation *self_ = nullptr; - Depsgraph *depsgraph_ = nullptr; - Scene *scene_ = nullptr; - Object *object_ = nullptr; + const Depsgraph *depsgraph_ = nullptr; + const Scene *scene_ = nullptr; ARegion *region_ = nullptr; - View3D *v3d_ = nullptr; - RegionView3D *rv3d_ = nullptr; + const View3D *v3d_ = nullptr; + const RegionView3D *rv3d_ = nullptr; + Object *object_ = nullptr; Curves *curves_id_ = nullptr; CurvesGeometry *curves_ = nullptr; - Brush *brush_ = nullptr; + const Brush *brush_ = nullptr; float brush_radius_re_; float brush_radius_sq_re_; float brush_strength_; @@ -297,17 +299,19 @@ struct CurvesEffectOperationExecutor { Vector<float> move_distances_cu; }; - void execute(CurvesEffectOperation &self, bContext *C, const StrokeExtension &stroke_extension) + void execute(CurvesEffectOperation &self, + const bContext &C, + const StrokeExtension &stroke_extension) { BLI_SCOPED_DEFER([&]() { self.last_mouse_position_ = stroke_extension.mouse_position; }); self_ = &self; - depsgraph_ = CTX_data_depsgraph_pointer(C); - scene_ = CTX_data_scene(C); - object_ = CTX_data_active_object(C); - region_ = CTX_wm_region(C); - v3d_ = CTX_wm_view3d(C); - rv3d_ = CTX_wm_region_view3d(C); + depsgraph_ = CTX_data_depsgraph_pointer(&C); + scene_ = CTX_data_scene(&C); + object_ = CTX_data_active_object(&C); + region_ = CTX_wm_region(&C); + v3d_ = CTX_wm_view3d(&C); + rv3d_ = CTX_wm_region_view3d(&C); curves_id_ = static_cast<Curves *>(object_->data); curves_ = &CurvesGeometry::wrap(curves_id_->geometry); @@ -315,8 +319,8 @@ struct CurvesEffectOperationExecutor { return; } - CurvesSculpt &curves_sculpt = *scene_->toolsettings->curves_sculpt; - brush_ = BKE_paint_brush(&curves_sculpt.paint); + const CurvesSculpt &curves_sculpt = *scene_->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt.paint); brush_radius_re_ = BKE_brush_size_get(scene_, brush_); brush_strength_ = BKE_brush_alpha_get(scene_, brush_); brush_radius_sq_re_ = pow2f(brush_radius_re_); @@ -331,7 +335,13 @@ struct CurvesEffectOperationExecutor { if (stroke_extension.is_first) { if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { if (std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( - *C, *object_, stroke_extension.mouse_position, brush_radius_re_)) { + *depsgraph_, + *region_, + *v3d_, + *rv3d_, + *object_, + stroke_extension.mouse_position, + brush_radius_re_)) { self.brush_3d_ = *brush_3d; } } @@ -356,6 +366,7 @@ struct CurvesEffectOperationExecutor { curves_->tag_positions_changed(); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); ED_region_tag_redraw(region_); } @@ -367,6 +378,13 @@ struct CurvesEffectOperationExecutor { float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + Vector<float4x4> symmetry_brush_transforms_inv; + for (const float4x4 brush_transform : symmetry_brush_transforms) { + symmetry_brush_transforms_inv.append(brush_transform.inverted()); + } + threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { Influences &local_influences = influences_for_thread.local(); @@ -374,55 +392,59 @@ struct CurvesEffectOperationExecutor { const IndexRange points = curves_->points_for_curve(curve_i); const int tot_segments = points.size() - 1; float max_move_distance_cu = 0.0f; - for (const int segment_i : IndexRange(tot_segments)) { - const float3 &p1_cu = positions_cu[points[segment_i]]; - const float3 &p2_cu = positions_cu[points[segment_i] + 1]; - - float2 p1_re, p2_re; - ED_view3d_project_float_v2_m4(region_, p1_cu, p1_re, projection.values); - ED_view3d_project_float_v2_m4(region_, p2_cu, p2_re, projection.values); - - float2 closest_on_brush_re; - float2 closest_on_segment_re; - float lambda_on_brush; - float lambda_on_segment; - const float dist_to_brush_sq_re = closest_seg_seg_v2(closest_on_brush_re, - closest_on_segment_re, - &lambda_on_brush, - &lambda_on_segment, - brush_pos_start_re_, - brush_pos_end_re_, - p1_re, - p2_re); - - if (dist_to_brush_sq_re > brush_radius_sq_re_) { - continue; - } - const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); - const float radius_falloff = BKE_brush_curve_strength( - brush_, dist_to_brush_re, brush_radius_re_); - const float weight = brush_strength_ * radius_falloff; - - const float3 closest_on_segment_cu = math::interpolate(p1_cu, p2_cu, lambda_on_segment); - - float3 brush_start_pos_wo, brush_end_pos_wo; - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * closest_on_segment_cu, - brush_pos_start_re_, - brush_start_pos_wo); - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * closest_on_segment_cu, - brush_pos_end_re_, - brush_end_pos_wo); - const float3 brush_start_pos_cu = world_to_curves_mat_ * brush_start_pos_wo; - const float3 brush_end_pos_cu = world_to_curves_mat_ * brush_end_pos_wo; - - const float move_distance_cu = weight * - math::distance(brush_start_pos_cu, brush_end_pos_cu); - max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu); + for (const float4x4 &brush_transform_inv : symmetry_brush_transforms_inv) { + for (const int segment_i : IndexRange(tot_segments)) { + const float3 &p1_cu = brush_transform_inv * positions_cu[points[segment_i]]; + const float3 &p2_cu = brush_transform_inv * positions_cu[points[segment_i] + 1]; + + float2 p1_re, p2_re; + ED_view3d_project_float_v2_m4(region_, p1_cu, p1_re, projection.values); + ED_view3d_project_float_v2_m4(region_, p2_cu, p2_re, projection.values); + + float2 closest_on_brush_re; + float2 closest_on_segment_re; + float lambda_on_brush; + float lambda_on_segment; + const float dist_to_brush_sq_re = closest_seg_seg_v2(closest_on_brush_re, + closest_on_segment_re, + &lambda_on_brush, + &lambda_on_segment, + brush_pos_start_re_, + brush_pos_end_re_, + p1_re, + p2_re); + + if (dist_to_brush_sq_re > brush_radius_sq_re_) { + continue; + } + + const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_re, brush_radius_re_); + const float weight = brush_strength_ * radius_falloff; + + const float3 closest_on_segment_cu = math::interpolate( + p1_cu, p2_cu, lambda_on_segment); + + float3 brush_start_pos_wo, brush_end_pos_wo; + ED_view3d_win_to_3d(v3d_, + region_, + curves_to_world_mat_ * closest_on_segment_cu, + brush_pos_start_re_, + brush_start_pos_wo); + ED_view3d_win_to_3d(v3d_, + region_, + curves_to_world_mat_ * closest_on_segment_cu, + brush_pos_end_re_, + brush_end_pos_wo); + const float3 brush_start_pos_cu = world_to_curves_mat_ * brush_start_pos_wo; + const float3 brush_end_pos_cu = world_to_curves_mat_ * brush_end_pos_wo; + + const float move_distance_cu = weight * + math::distance(brush_start_pos_cu, brush_end_pos_cu); + max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu); + } } if (max_move_distance_cu > 0.0f) { local_influences.curve_indices.append(curve_i); @@ -454,6 +476,10 @@ struct CurvesEffectOperationExecutor { const float brush_pos_diff_length_cu = math::length(brush_pos_diff_cu); const float brush_radius_cu = self_->brush_3d_.radius_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { Influences &local_influences = influences_for_thread.local(); @@ -461,32 +487,37 @@ struct CurvesEffectOperationExecutor { const IndexRange points = curves_->points_for_curve(curve_i); const int tot_segments = points.size() - 1; float max_move_distance_cu = 0.0f; - for (const int segment_i : IndexRange(tot_segments)) { - const float3 &p1_cu = positions_cu[points[segment_i]]; - const float3 &p2_cu = positions_cu[points[segment_i] + 1]; - - float3 closest_on_segment_cu; - float3 closest_on_brush_cu; - isect_seg_seg_v3(p1_cu, - p2_cu, - brush_pos_start_cu, - brush_pos_end_cu, - closest_on_segment_cu, - closest_on_brush_cu); - - const float dist_to_brush_sq_cu = math::distance_squared(closest_on_segment_cu, - closest_on_brush_cu); - if (dist_to_brush_sq_cu > brush_radius_sq_cu) { - continue; + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + const float3 brush_pos_start_transformed_cu = brush_transform * brush_pos_start_cu; + const float3 brush_pos_end_transformed_cu = brush_transform * brush_pos_end_cu; + + for (const int segment_i : IndexRange(tot_segments)) { + const float3 &p1_cu = positions_cu[points[segment_i]]; + const float3 &p2_cu = positions_cu[points[segment_i] + 1]; + + float3 closest_on_segment_cu; + float3 closest_on_brush_cu; + isect_seg_seg_v3(p1_cu, + p2_cu, + brush_pos_start_transformed_cu, + brush_pos_end_transformed_cu, + closest_on_segment_cu, + closest_on_brush_cu); + + const float dist_to_brush_sq_cu = math::distance_squared(closest_on_segment_cu, + closest_on_brush_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + continue; + } + + const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_cu, brush_radius_cu); + const float weight = brush_strength_ * radius_falloff; + + const float move_distance_cu = weight * brush_pos_diff_length_cu; + max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu); } - - const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); - const float radius_falloff = BKE_brush_curve_strength( - brush_, dist_to_brush_cu, brush_radius_cu); - const float weight = brush_strength_ * radius_falloff; - - const float move_distance_cu = weight * brush_pos_diff_length_cu; - max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu); } if (max_move_distance_cu > 0.0f) { local_influences.curve_indices.append(curve_i); @@ -497,7 +528,7 @@ struct CurvesEffectOperationExecutor { } }; -void CurvesEffectOperation::on_stroke_extended(bContext *C, +void CurvesEffectOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) { CurvesEffectOperationExecutor executor; @@ -505,10 +536,10 @@ void CurvesEffectOperation::on_stroke_extended(bContext *C, } std::unique_ptr<CurvesSculptStrokeOperation> new_grow_shrink_operation( - const BrushStrokeMode brush_mode, bContext *C) + const BrushStrokeMode brush_mode, const bContext &C) { - Scene &scene = *CTX_data_scene(C); - Brush &brush = *BKE_paint_brush(&scene.toolsettings->curves_sculpt->paint); + const Scene &scene = *CTX_data_scene(&C); + const Brush &brush = *BKE_paint_brush_for_read(&scene.toolsettings->curves_sculpt->paint); const bool use_scale_uniform = brush.curves_sculpt_settings->flag & BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM; const bool use_grow = (brush_mode == BRUSH_STROKE_INVERT) == ((brush.flag & BRUSH_DIR_IN) != 0); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh index 03413221907..00e7213b5e6 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh @@ -13,6 +13,8 @@ struct ARegion; struct RegionView3D; +struct Depsgraph; +struct View3D; struct Object; namespace blender::ed::sculpt_paint { @@ -30,15 +32,16 @@ struct StrokeExtension { class CurvesSculptStrokeOperation { public: virtual ~CurvesSculptStrokeOperation() = default; - virtual void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) = 0; + virtual void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) = 0; }; -std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(); +std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C, + ReportList *reports); std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_grow_shrink_operation( - const BrushStrokeMode brush_mode, bContext *C); + const BrushStrokeMode brush_mode, const bContext &C); struct CurvesBrush3D { float3 position_cu; @@ -48,9 +51,14 @@ struct CurvesBrush3D { /** * Find 3d brush position based on cursor position for curves sculpting. */ -std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, - Object &curves_object, +std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, + const ARegion ®ion, + const View3D &v3d, + const RegionView3D &rv3d, + const Object &curves_object, const float2 &brush_pos_re, - float brush_radius_re); + const float brush_radius_re); + +Vector<float4x4> get_symmetry_brush_transforms(eCurvesSymmetryType symmetry); } // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 992ac77803a..776da37205c 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -74,12 +74,12 @@ using blender::bke::CurvesGeometry; /** \name * SCULPT_CURVES_OT_brush_stroke * \{ */ -static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext *C, - wmOperator *op) +static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext &C, + wmOperator &op) { - const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op->ptr, "mode")); + const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op.ptr, "mode")); - Scene &scene = *CTX_data_scene(C); + Scene &scene = *CTX_data_scene(&C); CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); switch (brush.curves_sculpt_tool) { @@ -90,7 +90,7 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bConte case CURVES_SCULPT_TOOL_SNAKE_HOOK: return new_snake_hook_operation(); case CURVES_SCULPT_TOOL_ADD: - return new_add_operation(); + return new_add_operation(C, op.reports); case CURVES_SCULPT_TOOL_GROW_SHRINK: return new_grow_shrink_operation(mode, C); } @@ -131,14 +131,14 @@ static void stroke_update_step(bContext *C, if (!op_data->operation) { stroke_extension.is_first = true; - op_data->operation = start_brush_operation(C, op); + op_data->operation = start_brush_operation(*C, *op); } else { stroke_extension.is_first = false; } if (op_data->operation) { - op_data->operation->on_stroke_extended(C, stroke_extension); + op_data->operation->on_stroke_extended(*C, stroke_extension); } } @@ -149,6 +149,12 @@ static void stroke_done(const bContext *C, PaintStroke *stroke) static int sculpt_curves_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + if (brush == nullptr) { + return OPERATOR_CANCELLED; + } + SculptCurvesBrushStrokeData *op_data = MEM_new<SculptCurvesBrushStrokeData>(__func__); op_data->stroke = paint_stroke_new(C, op, diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc index 6d930d35f04..e009b443839 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc @@ -37,6 +37,8 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "WM_api.h" + /** * The code below uses a prefix naming convention to indicate the coordinate space: * - `cu`: Local space of the curves object that is being edited. @@ -61,7 +63,7 @@ class SnakeHookOperation : public CurvesSculptStrokeOperation { friend struct SnakeHookOperatorExecutor; public: - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; /** @@ -70,19 +72,19 @@ class SnakeHookOperation : public CurvesSculptStrokeOperation { */ struct SnakeHookOperatorExecutor { SnakeHookOperation *self_ = nullptr; - bContext *C_ = nullptr; - Scene *scene_ = nullptr; - Object *object_ = nullptr; + const Depsgraph *depsgraph_ = nullptr; + const Scene *scene_ = nullptr; ARegion *region_ = nullptr; - View3D *v3d_ = nullptr; - RegionView3D *rv3d_ = nullptr; + const View3D *v3d_ = nullptr; + const RegionView3D *rv3d_ = nullptr; - CurvesSculpt *curves_sculpt_ = nullptr; - Brush *brush_ = nullptr; + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; float brush_radius_re_; float brush_strength_; eBrushFalloffShape falloff_shape_; + Object *object_ = nullptr; Curves *curves_id_ = nullptr; CurvesGeometry *curves_ = nullptr; @@ -93,20 +95,23 @@ struct SnakeHookOperatorExecutor { float2 brush_pos_re_; float2 brush_pos_diff_re_; - void execute(SnakeHookOperation &self, bContext *C, const StrokeExtension &stroke_extension) + void execute(SnakeHookOperation &self, + const bContext &C, + const StrokeExtension &stroke_extension) { BLI_SCOPED_DEFER([&]() { self.last_mouse_position_re_ = stroke_extension.mouse_position; }); self_ = &self; - C_ = C; - scene_ = CTX_data_scene(C); - object_ = CTX_data_active_object(C); - region_ = CTX_wm_region(C); - v3d_ = CTX_wm_view3d(C); - rv3d_ = CTX_wm_region_view3d(C); + depsgraph_ = CTX_data_depsgraph_pointer(&C); + scene_ = CTX_data_scene(&C); + scene_ = CTX_data_scene(&C); + object_ = CTX_data_active_object(&C); + region_ = CTX_wm_region(&C); + v3d_ = CTX_wm_view3d(&C); + rv3d_ = CTX_wm_region_view3d(&C); curves_sculpt_ = scene_->toolsettings->curves_sculpt; - brush_ = BKE_paint_brush(&curves_sculpt_->paint); + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); brush_radius_re_ = BKE_brush_size_get(scene_, brush_); brush_strength_ = BKE_brush_alpha_get(scene_, brush_); falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape); @@ -127,7 +132,7 @@ struct SnakeHookOperatorExecutor { if (stroke_extension.is_first) { if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( - *C_, *object_, brush_pos_re_, brush_radius_re_); + *depsgraph_, *region_, *v3d_, *rv3d_, *object_, brush_pos_re_, brush_radius_re_); if (brush_3d.has_value()) { self_->brush_3d_ = *brush_3d; } @@ -136,10 +141,10 @@ struct SnakeHookOperatorExecutor { } if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { - this->spherical_snake_hook(); + this->spherical_snake_hook_with_symmetry(); } else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) { - this->projected_snake_hook(); + this->projected_snake_hook_with_symmetry(); } else { BLI_assert_unreachable(); @@ -147,11 +152,23 @@ struct SnakeHookOperatorExecutor { curves_->tag_positions_changed(); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); ED_region_tag_redraw(region_); } - void projected_snake_hook() + void projected_snake_hook_with_symmetry() + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->projected_snake_hook(brush_transform); + } + } + + void projected_snake_hook(const float4x4 &brush_transform) { + const float4x4 brush_transform_inv = brush_transform.inverted(); + MutableSpan<float3> positions_cu = curves_->positions_for_write(); float4x4 projection; @@ -161,7 +178,7 @@ struct SnakeHookOperatorExecutor { for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); const int last_point_i = points.last(); - const float3 old_pos_cu = positions_cu[last_point_i]; + const float3 old_pos_cu = brush_transform_inv * positions_cu[last_point_i]; float2 old_pos_re; ED_view3d_project_float_v2_m4(region_, old_pos_cu, old_pos_re, projection.values); @@ -179,17 +196,15 @@ struct SnakeHookOperatorExecutor { float3 new_position_wo; ED_view3d_win_to_3d( v3d_, region_, curves_to_world_mat_ * old_pos_cu, new_position_re, new_position_wo); - const float3 new_position_cu = world_to_curves_mat_ * new_position_wo; + const float3 new_position_cu = brush_transform * (world_to_curves_mat_ * new_position_wo); this->move_last_point_and_resample(positions_cu.slice(points), new_position_cu); } }); } - void spherical_snake_hook() + void spherical_snake_hook_with_symmetry() { - MutableSpan<float3> positions_cu = curves_->positions_for_write(); - float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); @@ -206,9 +221,23 @@ struct SnakeHookOperatorExecutor { brush_end_wo); const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; - const float3 brush_diff_cu = brush_end_cu - brush_start_cu; const float brush_radius_cu = self_->brush_3d_.radius_cu; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->spherical_snake_hook( + brush_transform * brush_start_cu, brush_transform * brush_end_cu, brush_radius_cu); + } + } + + void spherical_snake_hook(const float3 &brush_start_cu, + const float3 &brush_end_cu, + const float brush_radius_cu) + { + MutableSpan<float3> positions_cu = curves_->positions_for_write(); + const float3 brush_diff_cu = brush_end_cu - brush_start_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { @@ -269,7 +298,8 @@ struct SnakeHookOperatorExecutor { } }; -void SnakeHookOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) +void SnakeHookOperation::on_stroke_extended(const bContext &C, + const StrokeExtension &stroke_extension) { SnakeHookOperatorExecutor executor; executor.execute(*this, C, stroke_extension); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index e442cd53639..95a7c1d8dee 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -7,6 +7,7 @@ */ #include <float.h> +#include <limits.h> #include <math.h> #include <stdio.h> #include <string.h> @@ -35,12 +36,17 @@ #include "IMB_imbuf_types.h" #include "DNA_brush_types.h" +#include "DNA_customdata_types.h" +#include "DNA_defs.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_node_types.h" +#include "DNA_object_enums.h" #include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_brush.h" #include "BKE_camera.h" #include "BKE_colorband.h" @@ -61,6 +67,8 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -76,12 +84,18 @@ #include "GPU_capabilities.h" #include "GPU_init_exit.h" +#include "NOD_shader.h" + +#include "UI_interface.h" +#include "UI_resources.h" + #include "WM_api.h" #include "WM_types.h" #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "RNA_types.h" #include "NOD_shader.h" @@ -4070,7 +4084,7 @@ typedef struct { static void proj_paint_layer_clone_init(ProjPaintState *ps, ProjPaintLayerClone *layer_clone) { - MLoopUV *mloopuv_clone_base = NULL; + const MLoopUV *mloopuv_clone_base = NULL; /* use clone mtface? */ if (ps->do_layer_clone) { @@ -6472,6 +6486,38 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data) return ima; } +static CustomDataLayer *proj_paint_color_attribute_create(wmOperator *op, Object *ob) +{ + char name[MAX_NAME] = ""; + float color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + AttributeDomain domain = ATTR_DOMAIN_POINT; + CustomDataType type = CD_PROP_COLOR; + + if (op) { + RNA_string_get(op->ptr, "name", name); + RNA_float_get_array(op->ptr, "color", color); + domain = (AttributeDomain)RNA_enum_get(op->ptr, "domain"); + type = (CustomDataType)RNA_enum_get(op->ptr, "data_type"); + } + + ID *id = (ID *)ob->data; + CustomDataLayer *layer = BKE_id_attribute_new(id, name, type, domain, op->reports); + + if (!layer) { + return NULL; + } + + BKE_id_attributes_active_color_set(id, layer); + + if (!BKE_id_attributes_render_color_get(id)) { + BKE_id_attributes_render_color_set(id, layer); + } + + BKE_object_attributes_active_color_fill(ob, color, false); + + return layer; +} + /** * Get a default color for the paint slot layer from a material's Principled BSDF. * @@ -6539,6 +6585,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Material *ma; Image *ima = NULL; + CustomDataLayer *layer = NULL; if (!ob) { return false; @@ -6551,7 +6598,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) int type = RNA_enum_get(op->ptr, "type"); bool is_data = (type > LAYER_BASE_COLOR); - bNode *imanode; + bNode *new_node; bNodeTree *ntree = ma->nodetree; if (!ntree) { @@ -6561,17 +6608,36 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) ma->use_nodes = true; - /* try to add an image node */ - imanode = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); - - ima = proj_paint_image_create(op, bmain, is_data); - imanode->id = &ima->id; - - nodeSetActive(ntree, imanode); + const ePaintCanvasSource slot_type = ob->mode == OB_MODE_SCULPT ? + (ePaintCanvasSource)RNA_enum_get(op->ptr, + "slot_type") : + PAINT_CANVAS_SOURCE_IMAGE; + + /* Create a new node. */ + switch (slot_type) { + case PAINT_CANVAS_SOURCE_IMAGE: { + new_node = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); + ima = proj_paint_image_create(op, bmain, is_data); + new_node->id = &ima->id; + break; + } + case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE: { + new_node = nodeAddStaticNode(C, ntree, SH_NODE_ATTRIBUTE); + if ((layer = proj_paint_color_attribute_create(op, ob))) { + BLI_strncpy_utf8( + ((NodeShaderAttribute *)new_node->storage)->name, layer->name, MAX_NAME); + } + break; + } + case PAINT_CANVAS_SOURCE_MATERIAL: + BLI_assert_unreachable(); + return false; + } + nodeSetActive(ntree, new_node); /* Connect to first available principled BSDF node. */ bNode *in_node = ntreeFindType(ntree, SH_NODE_BSDF_PRINCIPLED); - bNode *out_node = imanode; + bNode *out_node = new_node; if (in_node != NULL) { bNodeSocket *out_sock = nodeFindSocket(out_node, SOCK_OUT, "Color"); @@ -6634,6 +6700,11 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_USER_NEW_IMAGE); WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima); } + if (layer) { + BKE_texpaint_slot_refresh_cache(scene, ma, ob); + DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, ob->data); + } DEG_id_tag_update(&ntree->id, 0); DEG_id_tag_update(&ma->id, ID_RECALC_SHADING); @@ -6695,6 +6766,44 @@ static int texture_paint_add_texture_paint_slot_invoke(bContext *C, return WM_operator_props_dialog_popup(C, op, 300); } +static void texture_paint_add_texture_paint_slot_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + Object *ob = ED_object_active_context(C); + ePaintCanvasSource slot_type = PAINT_CANVAS_SOURCE_IMAGE; + + if (ob->mode == OB_MODE_SCULPT) { + slot_type = (ePaintCanvasSource)RNA_enum_get(op->ptr, "slot_type"); + uiItemR(layout, op->ptr, "slot_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + } + + uiItemR(layout, op->ptr, "name", 0, NULL, ICON_NONE); + + switch (slot_type) { + case PAINT_CANVAS_SOURCE_IMAGE: { + uiLayout *col = uiLayoutColumn(layout, true); + uiItemR(col, op->ptr, "width", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "height", 0, NULL, ICON_NONE); + + uiItemR(layout, op->ptr, "alpha", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "generated_type", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "float", 0, NULL, ICON_NONE); + break; + } + case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE: + uiItemR(layout, op->ptr, "domain", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "data_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + break; + case PAINT_CANVAS_SOURCE_MATERIAL: + BLI_assert_unreachable(); + break; + } + + uiItemR(layout, op->ptr, "color", 0, NULL, ICON_NONE); +} + #define IMA_DEF_NAME N_("Untitled") void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot) @@ -6702,40 +6811,92 @@ void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot) PropertyRNA *prop; static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + static const EnumPropertyItem slot_type_items[3] = { + {PAINT_CANVAS_SOURCE_IMAGE, "IMAGE", 0, "Image", ""}, + {PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE, "COLOR_ATTRIBUTE", 0, "Color Attribute", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem domain_items[3] = { + {ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", ""}, + {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem attribute_type_items[3] = { + {CD_PROP_COLOR, "COLOR", 0, "Color", ""}, + {CD_PROP_BYTE_COLOR, "BYTE_COLOR", 0, "Byte Color", ""}, + {0, NULL, 0, NULL, NULL}, + }; + /* identifiers */ - ot->name = "Add Texture Paint Slot"; - ot->description = "Add a texture paint slot"; + ot->name = "Add Paint Slot"; + ot->description = "Add a paint slot"; ot->idname = "PAINT_OT_add_texture_paint_slot"; /* api callbacks */ ot->invoke = texture_paint_add_texture_paint_slot_invoke; ot->exec = texture_paint_add_texture_paint_slot_exec; ot->poll = ED_operator_object_active_editable_mesh; + ot->ui = texture_paint_add_texture_paint_slot_ui; /* flags */ ot->flag = OPTYPE_UNDO; - /* properties */ - prop = RNA_def_enum(ot->srna, "type", layer_type_items, 0, "Type", "Merge method to use"); + /* Shared Properties */ + prop = RNA_def_enum(ot->srna, + "type", + layer_type_items, + 0, + "Material Layer Type", + "Material layer type of new paint slot"); RNA_def_property_flag(prop, PROP_HIDDEN); - RNA_def_string(ot->srna, "name", IMA_DEF_NAME, MAX_ID_NAME - 2, "Name", "Image data-block name"); - prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); - RNA_def_property_subtype(prop, PROP_PIXEL); - prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); - RNA_def_property_subtype(prop, PROP_PIXEL); + + prop = RNA_def_enum( + ot->srna, "slot_type", slot_type_items, 0, "Slot Type", "Type of new paint slot"); + + prop = RNA_def_string( + ot->srna, "name", IMA_DEF_NAME, MAX_NAME, "Name", "Name for new paint slot source"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_float_color( ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); RNA_def_property_float_array_default(prop, default_color); + + /* Image Properties */ + prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + + prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + RNA_def_boolean(ot->srna, "alpha", true, "Alpha", "Create an image with an alpha channel"); + RNA_def_enum(ot->srna, "generated_type", rna_enum_image_generated_type_items, IMA_GENTYPE_BLANK, "Generated Type", "Fill the image with a grid for UV map testing"); + RNA_def_boolean( ot->srna, "float", 0, "32-bit Float", "Create image with 32-bit floating-point bit depth"); + + /* Color Attribute Properties */ + RNA_def_enum(ot->srna, + "domain", + domain_items, + ATTR_DOMAIN_POINT, + "Domain", + "Type of element that attribute is stored on"); + + RNA_def_enum(ot->srna, + "data_type", + attribute_type_items, + CD_PROP_COLOR, + "Data Type", + "Type of data stored in attribute"); } static int add_simple_uvs_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 747295f3de0..16b22775b9e 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -4145,13 +4145,7 @@ static bool vertex_color_set(Object *ob, ColorPaint4f paintcol_in, Color *color_ } /** - * Fills the object's active color atribute layer with the fill color. - * - * \param[in] ob: The object. - * \param[in] fill_color: The fill color. - * \param[in] only_selected: Limit the fill to selected faces or vertices. - * - * \return #true if successful. + * See doc-string for #BKE_object_attributes_active_color_fill. */ static bool paint_object_attributes_active_color_fill_ex(Object *ob, ColorPaint4f fill_color, diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 85ea5d5bfc6..5bb4eb7cebf 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -3248,8 +3248,8 @@ static void do_brush_action(Sculpt *sd, } nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode); } - - if (sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob)) { + const bool use_pixels = sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob); + if (use_pixels) { sculpt_pbvh_update_pixels(paint_mode_settings, ss, ob); } @@ -3302,16 +3302,18 @@ static void do_brush_action(Sculpt *sd, } float location[3]; - SculptThreadedTaskData task_data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - }; + if (!use_pixels) { + SculptThreadedTaskData task_data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); + } if (sculpt_brush_needs_normal(ss, brush)) { update_sculpt_normal(sd, ob, nodes, totnode); @@ -5276,6 +5278,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); + ToolSettings *tool_settings = CTX_data_tool_settings(C); /* NOTE: This should be removed when paint mode is available. Paint mode can force based on the * canvas it is painting on. (ref. use_sculpt_texture_paint). */ @@ -5293,7 +5296,15 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f SculptCursorGeometryInfo sgi; SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); - SCULPT_undo_push_begin(ob, sculpt_tool_name(sd)); + /* Setup the correct undo system. Image painting and sculpting are mutual exclusive. + * Color attributes are part of the sculpting undo system. */ + if (brush && brush->sculpt_tool == SCULPT_TOOL_PAINT && + SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { + ED_image_undo_push_begin(op->type->name, PAINT_MODE_SCULPT); + } + else { + SCULPT_undo_push_begin(ob, sculpt_tool_name(sd)); + } return true; } @@ -5420,7 +5431,13 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str SCULPT_cache_free(ss->cache); ss->cache = NULL; - SCULPT_undo_push_end(ob); + if (brush && brush->sculpt_tool == SCULPT_TOOL_PAINT && + SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { + ED_image_undo_push_end(); + } + else { + SCULPT_undo_push_end(ob); + } if (brush->sculpt_tool == SCULPT_TOOL_MASK) { SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index 7171c241534..67bf7b3286c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -60,7 +60,7 @@ int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh) { - int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + const int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); if (!face_sets) { return SCULPT_FACE_SET_NONE; } diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index 5aa1dbef74c..8803c95aab1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -637,16 +637,16 @@ static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op)) if (MPropCol_layer_n == -1) { return OPERATOR_CANCELLED; } - MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); + const MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); - MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); - MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); + const MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); + const MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); for (int i = 0; i < mesh->totpoly; i++) { - MPoly *c_poly = &polys[i]; + const MPoly *c_poly = &polys[i]; for (int j = 0; j < c_poly->totloop; j++) { int loop_index = c_poly->loopstart + j; - MLoop *c_loop = &loops[c_poly->loopstart + j]; + const MLoop *c_loop = &loops[c_poly->loopstart + j]; float srgb_color[4]; linearrgb_to_srgb_v4(srgb_color, vertcols[c_loop->v].color); loopcols[loop_index].r = (char)(srgb_color[0] * 255); @@ -711,7 +711,8 @@ static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op)) if (mloopcol_layer_n == -1) { return OPERATOR_CANCELLED; } - MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_PROP_BYTE_COLOR, mloopcol_layer_n); + const MLoopCol *loopcols = CustomData_get_layer_n( + &mesh->ldata, CD_PROP_BYTE_COLOR, mloopcol_layer_n); const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR); if (MPropCol_layer_n == -1) { @@ -719,14 +720,14 @@ static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op)) } MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); - MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); - MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); + const MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); + const MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); for (int i = 0; i < mesh->totpoly; i++) { - MPoly *c_poly = &polys[i]; + const MPoly *c_poly = &polys[i]; for (int j = 0; j < c_poly->totloop; j++) { int loop_index = c_poly->loopstart + j; - MLoop *c_loop = &loops[c_poly->loopstart + j]; + const MLoop *c_loop = &loops[c_poly->loopstart + j]; vertcols[c_loop->v].color[0] = (loopcols[loop_index].r / 255.0f); vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f); vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f); diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc index df1ccc0fbe9..b0c33a65bc9 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc @@ -360,6 +360,86 @@ static void do_paint_pixels(void *__restrict userdata, node_data.flags.dirty |= pixels_updated; } +static void undo_region_tiles( + ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th) +{ + int srcx = 0, srcy = 0; + IMB_rectclip(ibuf, nullptr, &x, &y, &srcx, &srcy, &w, &h); + *tw = ((x + w - 1) >> ED_IMAGE_UNDO_TILE_BITS); + *th = ((y + h - 1) >> ED_IMAGE_UNDO_TILE_BITS); + *tx = (x >> ED_IMAGE_UNDO_TILE_BITS); + *ty = (y >> ED_IMAGE_UNDO_TILE_BITS); +} + +static void push_undo(const NodeData &node_data, + Image &image, + ImageUser &image_user, + const image::ImageTileWrapper &image_tile, + ImBuf &image_buffer, + ImBuf **tmpibuf) +{ + for (const UDIMTileUndo &tile_undo : node_data.undo_regions) { + if (tile_undo.tile_number != image_tile.get_tile_number()) { + continue; + } + int tilex, tiley, tilew, tileh; + ListBase *undo_tiles = ED_image_paint_tile_list_get(); + undo_region_tiles(&image_buffer, + tile_undo.region.xmin, + tile_undo.region.ymin, + BLI_rcti_size_x(&tile_undo.region), + BLI_rcti_size_y(&tile_undo.region), + &tilex, + &tiley, + &tilew, + &tileh); + for (int ty = tiley; ty <= tileh; ty++) { + for (int tx = tilex; tx <= tilew; tx++) { + ED_image_paint_tile_push(undo_tiles, + &image, + &image_buffer, + tmpibuf, + &image_user, + tx, + ty, + nullptr, + nullptr, + true, + true); + } + } + } +} + +static void do_push_undo_tile(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + TexturePaintingUserData *data = static_cast<TexturePaintingUserData *>(userdata); + PBVHNode *node = data->nodes[n]; + + NodeData &node_data = BKE_pbvh_pixels_node_data_get(*node); + Image *image = data->image_data.image; + ImageUser *image_user = data->image_data.image_user; + + ImBuf *tmpibuf = nullptr; + ImageUser local_image_user = *image_user; + LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) { + image::ImageTileWrapper image_tile(tile); + local_image_user.tile = image_tile.get_tile_number(); + ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &local_image_user, nullptr); + if (image_buffer == nullptr) { + continue; + } + + push_undo(node_data, *image, *image_user, image_tile, *image_buffer, &tmpibuf); + BKE_image_release_ibuf(image, image_buffer, nullptr); + } + if (tmpibuf) { + IMB_freeImBuf(tmpibuf); + } +} + static void do_mark_dirty_regions(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) @@ -421,6 +501,7 @@ void SCULPT_do_paint_brush_image( TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_push_undo_tile, &settings); BLI_task_parallel_range(0, totnode, &data, do_paint_pixels, &settings); TaskParallelSettings settings_flush; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 5867dc558de..1fee20444e7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -1265,7 +1265,7 @@ static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType typ unode->face_sets = MEM_callocN(me->totpoly * sizeof(int), "sculpt face sets"); - int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); + const int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); for (int i = 0; i < me->totpoly; i++) { unode->face_sets[i] = face_sets[i]; } diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c index e24d461019b..72df2b74b11 100644 --- a/source/blender/editors/space_clip/clip_buttons.c +++ b/source/blender/editors/space_clip/clip_buttons.c @@ -303,7 +303,7 @@ static void marker_block_handler(bContext *C, void *arg_cb, int event) cb->marker->pattern_corners[a][1] *= scale_y; } - BKE_tracking_marker_clamp(cb->marker, CLAMP_PAT_DIM); + BKE_tracking_marker_clamp_search_size(cb->marker); ok = true; } @@ -319,7 +319,7 @@ static void marker_block_handler(bContext *C, void *arg_cb, int event) sub_v2_v2v2(cb->marker->search_min, delta, side); add_v2_v2v2(cb->marker->search_max, delta, side); - BKE_tracking_marker_clamp(cb->marker, CLAMP_SEARCH_POS); + BKE_tracking_marker_clamp_search_position(cb->marker); ok = true; } @@ -340,7 +340,7 @@ static void marker_block_handler(bContext *C, void *arg_cb, int event) cb->marker->search_max[0] += dim[0]; cb->marker->search_max[1] += dim[1]; - BKE_tracking_marker_clamp(cb->marker, CLAMP_SEARCH_DIM); + BKE_tracking_marker_clamp_search_size(cb->marker); ok = true; } diff --git a/source/blender/editors/space_clip/clip_intern.h b/source/blender/editors/space_clip/clip_intern.h index dd01c095479..7f9cf61b748 100644 --- a/source/blender/editors/space_clip/clip_intern.h +++ b/source/blender/editors/space_clip/clip_intern.h @@ -187,8 +187,10 @@ void clip_draw_sfra_efra(struct View2D *v2d, struct Scene *scene); /* tracking_ops.c */ -struct MovieTrackingTrack *tracking_marker_check_slide( - struct bContext *C, const struct wmEvent *event, int *r_area, int *r_action, int *r_corner); +/* Find track which can be slid in a proximity of the given event. + * Uses the same distance tolerance rule as the "Slide Marker" operator. */ +struct MovieTrackingTrack *tracking_find_slidable_track_in_proximity(struct bContext *C, + const float co[2]); void CLIP_OT_add_marker(struct wmOperatorType *ot); void CLIP_OT_add_marker_at_click(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index 91fef23019c..5f52e1a3071 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -619,6 +619,44 @@ static void clip_dropboxes(void) WM_dropbox_add(lb, "CLIP_OT_open", clip_drop_poll, clip_drop_copy, NULL, NULL); } +static bool clip_set_region_visible(const bContext *C, + ARegion *region, + const bool is_visible, + const short alignment, + const bool view_all_on_show) +{ + bool view_changed = false; + + if (is_visible) { + if (region && (region->flag & RGN_FLAG_HIDDEN)) { + region->flag &= ~RGN_FLAG_HIDDEN; + region->v2d.flag &= ~V2D_IS_INIT; + if (view_all_on_show) { + region->v2d.cur = region->v2d.tot; + } + view_changed = true; + } + if (region && region->alignment != alignment) { + region->alignment = alignment; + view_changed = true; + } + } + else { + if (region && !(region->flag & RGN_FLAG_HIDDEN)) { + region->flag |= RGN_FLAG_HIDDEN; + region->v2d.flag &= ~V2D_IS_INIT; + WM_event_remove_handlers((bContext *)C, ®ion->handlers); + view_changed = true; + } + if (region && region->alignment != RGN_ALIGN_NONE) { + region->alignment = RGN_ALIGN_NONE; + view_changed = true; + } + } + + return view_changed; +} + static void clip_refresh(const bContext *C, ScrArea *area) { wmWindowManager *wm = CTX_wm_manager(C); @@ -662,127 +700,14 @@ static void clip_refresh(const bContext *C, ScrArea *area) break; } - if (main_visible) { - if (region_main && (region_main->flag & RGN_FLAG_HIDDEN)) { - region_main->flag &= ~RGN_FLAG_HIDDEN; - region_main->v2d.flag &= ~V2D_IS_INIT; - view_changed = true; - } - - if (region_main && region_main->alignment != RGN_ALIGN_NONE) { - region_main->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - else { - if (region_main && !(region_main->flag & RGN_FLAG_HIDDEN)) { - region_main->flag |= RGN_FLAG_HIDDEN; - region_main->v2d.flag &= ~V2D_IS_INIT; - WM_event_remove_handlers((bContext *)C, ®ion_main->handlers); - view_changed = true; - } - if (region_main && region_main->alignment != RGN_ALIGN_NONE) { - region_main->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - - if (properties_visible) { - if (region_properties && (region_properties->flag & RGN_FLAG_HIDDEN)) { - region_properties->flag &= ~RGN_FLAG_HIDDEN; - region_properties->v2d.flag &= ~V2D_IS_INIT; - view_changed = true; - } - if (region_properties && region_properties->alignment != RGN_ALIGN_RIGHT) { - region_properties->alignment = RGN_ALIGN_RIGHT; - view_changed = true; - } - } - else { - if (region_properties && !(region_properties->flag & RGN_FLAG_HIDDEN)) { - region_properties->flag |= RGN_FLAG_HIDDEN; - region_properties->v2d.flag &= ~V2D_IS_INIT; - WM_event_remove_handlers((bContext *)C, ®ion_properties->handlers); - view_changed = true; - } - if (region_properties && region_properties->alignment != RGN_ALIGN_NONE) { - region_properties->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - - if (tools_visible) { - if (region_tools && (region_tools->flag & RGN_FLAG_HIDDEN)) { - region_tools->flag &= ~RGN_FLAG_HIDDEN; - region_tools->v2d.flag &= ~V2D_IS_INIT; - view_changed = true; - } - if (region_tools && region_tools->alignment != RGN_ALIGN_LEFT) { - region_tools->alignment = RGN_ALIGN_LEFT; - view_changed = true; - } - } - else { - if (region_tools && !(region_tools->flag & RGN_FLAG_HIDDEN)) { - region_tools->flag |= RGN_FLAG_HIDDEN; - region_tools->v2d.flag &= ~V2D_IS_INIT; - WM_event_remove_handlers((bContext *)C, ®ion_tools->handlers); - view_changed = true; - } - if (region_tools && region_tools->alignment != RGN_ALIGN_NONE) { - region_tools->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - - if (preview_visible) { - if (region_preview && (region_preview->flag & RGN_FLAG_HIDDEN)) { - region_preview->flag &= ~RGN_FLAG_HIDDEN; - region_preview->v2d.flag &= ~V2D_IS_INIT; - region_preview->v2d.cur = region_preview->v2d.tot; - view_changed = true; - } - if (region_preview && region_preview->alignment != RGN_ALIGN_NONE) { - region_preview->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - else { - if (region_preview && !(region_preview->flag & RGN_FLAG_HIDDEN)) { - region_preview->flag |= RGN_FLAG_HIDDEN; - region_preview->v2d.flag &= ~V2D_IS_INIT; - WM_event_remove_handlers((bContext *)C, ®ion_preview->handlers); - view_changed = true; - } - if (region_preview && region_preview->alignment != RGN_ALIGN_NONE) { - region_preview->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - - if (channels_visible) { - if (region_channels && (region_channels->flag & RGN_FLAG_HIDDEN)) { - region_channels->flag &= ~RGN_FLAG_HIDDEN; - region_channels->v2d.flag &= ~V2D_IS_INIT; - view_changed = true; - } - if (region_channels && region_channels->alignment != RGN_ALIGN_LEFT) { - region_channels->alignment = RGN_ALIGN_LEFT; - view_changed = true; - } - } - else { - if (region_channels && !(region_channels->flag & RGN_FLAG_HIDDEN)) { - region_channels->flag |= RGN_FLAG_HIDDEN; - region_channels->v2d.flag &= ~V2D_IS_INIT; - WM_event_remove_handlers((bContext *)C, ®ion_channels->handlers); - view_changed = true; - } - if (region_channels && region_channels->alignment != RGN_ALIGN_NONE) { - region_channels->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } + view_changed |= clip_set_region_visible(C, region_main, main_visible, RGN_ALIGN_NONE, false); + view_changed |= clip_set_region_visible( + C, region_properties, properties_visible, RGN_ALIGN_RIGHT, false); + view_changed |= clip_set_region_visible(C, region_tools, tools_visible, RGN_ALIGN_LEFT, false); + view_changed |= clip_set_region_visible( + C, region_preview, preview_visible, RGN_ALIGN_NONE, true); + view_changed |= clip_set_region_visible( + C, region_channels, channels_visible, RGN_ALIGN_LEFT, false); if (view_changed) { ED_area_init(wm, window, area); diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index e7bdbfe7c68..ca224b04da5 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -336,35 +336,44 @@ void CLIP_OT_delete_marker(wmOperatorType *ot) /** \name Slide Marker Operator * \{ */ -enum { - SLIDE_ACTION_POS = 0, +typedef enum eSlideAction { + SLIDE_ACTION_NONE, + + SLIDE_ACTION_POS, SLIDE_ACTION_SIZE, SLIDE_ACTION_OFFSET, SLIDE_ACTION_TILT_SIZE, -}; +} eSlideAction; typedef struct { - short area, action; + short area; + eSlideAction action; MovieTrackingTrack *track; MovieTrackingMarker *marker; int mval[2]; int width, height; - float *min, *max, *pos, *offset, (*corners)[2]; - float spos[2]; + float *min, *max, *pos, (*corners)[2]; bool lock, accurate; /* Data to restore on cancel. */ - float old_search_min[2], old_search_max[2], old_pos[2], old_offset[2]; + float old_search_min[2], old_search_max[2], old_pos[2]; float old_corners[4][2]; float (*old_markers)[2]; } SlideMarkerData; -static void slide_marker_tilt_slider(const MovieTrackingMarker *marker, float r_slider[2]) +static void slide_marker_tilt_slider_relative(const float pattern_corners[4][2], float r_slider[2]) +{ + add_v2_v2v2(r_slider, pattern_corners[1], pattern_corners[2]); +} + +static void slide_marker_tilt_slider(const float marker_pos[2], + const float pattern_corners[4][2], + float r_slider[2]) { - add_v2_v2v2(r_slider, marker->pattern_corners[1], marker->pattern_corners[2]); - add_v2_v2(r_slider, marker->pos); + slide_marker_tilt_slider_relative(pattern_corners, r_slider); + add_v2_v2(r_slider, marker_pos); } static SlideMarkerData *create_slide_marker_data(SpaceClip *sc, @@ -373,7 +382,7 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc, const wmEvent *event, int area, int corner, - int action, + eSlideAction action, int width, int height) { @@ -389,29 +398,14 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc, if (area == TRACK_AREA_POINT) { data->pos = marker->pos; - data->offset = track->offset; } else if (area == TRACK_AREA_PAT) { - if (action == SLIDE_ACTION_SIZE) { - data->corners = marker->pattern_corners; - } - else if (action == SLIDE_ACTION_OFFSET) { - data->pos = marker->pos; - data->offset = track->offset; - data->old_markers = MEM_callocN(sizeof(*data->old_markers) * track->markersnr, - "slide markers"); - for (int a = 0; a < track->markersnr; a++) { - copy_v2_v2(data->old_markers[a], track->markers[a].pos); - } - } - else if (action == SLIDE_ACTION_POS) { + if (action == SLIDE_ACTION_POS) { data->corners = marker->pattern_corners; data->pos = marker->pattern_corners[corner]; - copy_v2_v2(data->spos, data->pos); } else if (action == SLIDE_ACTION_TILT_SIZE) { data->corners = marker->pattern_corners; - slide_marker_tilt_slider(marker, data->spos); } } else if (area == TRACK_AREA_SEARCH) { @@ -434,7 +428,6 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc, copy_v2_v2(data->old_search_min, marker->search_min); copy_v2_v2(data->old_search_max, marker->search_max); copy_v2_v2(data->old_pos, marker->pos); - copy_v2_v2(data->old_offset, track->offset); return data; } @@ -497,7 +490,7 @@ static int mouse_to_tilt_distance_squared(const MovieTrackingMarker *marker, int height) { float slider[2]; - slide_marker_tilt_slider(marker, slider); + slide_marker_tilt_slider(marker->pos, marker->pattern_corners, slider); return mouse_to_slide_zone_distance_squared(co, slider, width, height); } @@ -534,117 +527,96 @@ static bool slide_check_corners(float (*corners)[2]) return true; } -MovieTrackingTrack *tracking_marker_check_slide( - bContext *C, const wmEvent *event, int *r_area, int *r_action, int *r_corner) +static MovieTrackingTrack *tracking_marker_check_slide( + bContext *C, const float co[2], int *r_area, eSlideAction *r_action, int *r_corner) { const float distance_clip_squared = 12.0f * 12.0f; SpaceClip *sc = CTX_wm_space_clip(C); - ARegion *region = CTX_wm_region(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - MovieTrackingTrack *track; - int width, height; - float co[2]; ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking); - int framenr = ED_space_clip_get_clip_frame_number(sc); + const int framenr = ED_space_clip_get_clip_frame_number(sc); float global_min_distance_squared = FLT_MAX; - /* Sliding zone designator which is the closest to the mouse - * across all the tracks. - */ - int min_action = -1, min_area = 0, min_corner = -1; + /* Sliding zone designator which is the closest to the mouse across all the tracks. */ + eSlideAction min_action; + int min_area = 0, min_corner = -1; MovieTrackingTrack *min_track = NULL; + int width, height; ED_space_clip_get_size(sc, &width, &height); - if (width == 0 || height == 0) { return NULL; } - ED_clip_mouse_pos(sc, region, event->mval, co); + LISTBASE_FOREACH (MovieTrackingTrack *, track, tracksbase) { + if (!TRACK_VIEW_SELECTED(sc, track) || (track->flag & TRACK_LOCKED)) { + continue; + } - track = tracksbase->first; - while (track) { - if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) { - const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); - /* Sliding zone designator which is the closest to the mouse for - * the current tracks. - */ - float min_distance_squared = FLT_MAX; - int action = -1, area = 0, corner = -1; - - if ((marker->flag & MARKER_DISABLED) == 0) { - float distance_squared; - - /* We start checking with whether the mouse is close enough - * to the pattern offset area. - */ - distance_squared = mouse_to_offset_distance_squared(track, marker, co, width, height); - area = TRACK_AREA_POINT; - action = SLIDE_ACTION_POS; + const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); + if (marker->flag & MARKER_DISABLED) { + continue; + } - /* NOTE: All checks here are assuming there's no maximum distance - * limit, so checks are quite simple here. - * Actual distance clipping happens later once all the sliding - * zones are checked. - */ + /* We start checking with whether the mouse is close enough to the pattern offset area. */ + float distance_squared = mouse_to_offset_distance_squared(track, marker, co, width, height); + + /* Sliding zone designator which is the closest to the mouse for the current tracks. + * + * NOTE: All checks here are assuming there's no maximum distance limit, so checks are quite + * simple here. Actual distance clipping happens later once all the sliding zones are checked. + */ + float min_distance_squared = distance_squared; + int area = TRACK_AREA_POINT; + int action = SLIDE_ACTION_POS; + int corner = -1; + + /* If search area is visible, check how close to its sliding zones mouse is. */ + if (sc->flag & SC_SHOW_MARKER_SEARCH) { + distance_squared = mouse_to_search_corner_distance_squared(marker, co, 1, width, height); + if (distance_squared < min_distance_squared) { + area = TRACK_AREA_SEARCH; + action = SLIDE_ACTION_OFFSET; min_distance_squared = distance_squared; + } - /* If search area is visible, check how close to its sliding - * zones mouse is. - */ - if (sc->flag & SC_SHOW_MARKER_SEARCH) { - distance_squared = mouse_to_search_corner_distance_squared(marker, co, 1, width, height); - if (distance_squared < min_distance_squared) { - area = TRACK_AREA_SEARCH; - action = SLIDE_ACTION_OFFSET; - min_distance_squared = distance_squared; - } - - distance_squared = mouse_to_search_corner_distance_squared(marker, co, 0, width, height); - if (distance_squared < min_distance_squared) { - area = TRACK_AREA_SEARCH; - action = SLIDE_ACTION_SIZE; - min_distance_squared = distance_squared; - } - } - - /* If pattern area is visible, check which corner is closest to - * the mouse. - */ - if (sc->flag & SC_SHOW_MARKER_PATTERN) { - int current_corner = -1; - distance_squared = mouse_to_closest_pattern_corner_distance_squared( - marker, co, width, height, ¤t_corner); - if (distance_squared < min_distance_squared) { - area = TRACK_AREA_PAT; - action = SLIDE_ACTION_POS; - corner = current_corner; - min_distance_squared = distance_squared; - } + distance_squared = mouse_to_search_corner_distance_squared(marker, co, 0, width, height); + if (distance_squared < min_distance_squared) { + area = TRACK_AREA_SEARCH; + action = SLIDE_ACTION_SIZE; + min_distance_squared = distance_squared; + } + } - /* Here we also check whether the mouse is actually closer to - * the widget which controls scale and tilt. - */ - distance_squared = mouse_to_tilt_distance_squared(marker, co, width, height); - if (distance_squared < min_distance_squared) { - area = TRACK_AREA_PAT; - action = SLIDE_ACTION_TILT_SIZE; - min_distance_squared = distance_squared; - } - } + /* If pattern area is visible, check which corner is closest to the mouse. */ + if (sc->flag & SC_SHOW_MARKER_PATTERN) { + int current_corner = -1; + distance_squared = mouse_to_closest_pattern_corner_distance_squared( + marker, co, width, height, ¤t_corner); + if (distance_squared < min_distance_squared) { + area = TRACK_AREA_PAT; + action = SLIDE_ACTION_POS; + corner = current_corner; + min_distance_squared = distance_squared; + } - if (min_distance_squared < global_min_distance_squared) { - min_area = area; - min_action = action; - min_corner = corner; - min_track = track; - global_min_distance_squared = min_distance_squared; - } + /* Here we also check whether the mouse is actually closer to the widget which controls scale + * and tilt. */ + distance_squared = mouse_to_tilt_distance_squared(marker, co, width, height); + if (distance_squared < min_distance_squared) { + area = TRACK_AREA_PAT; + action = SLIDE_ACTION_TILT_SIZE; + min_distance_squared = distance_squared; } } - track = track->next; + if (min_distance_squared < global_min_distance_squared) { + min_area = area; + min_action = action; + min_corner = corner; + min_track = track; + global_min_distance_squared = min_distance_squared; + } } if (global_min_distance_squared < distance_clip_squared / sc->zoom) { @@ -659,9 +631,16 @@ MovieTrackingTrack *tracking_marker_check_slide( } return min_track; } + return NULL; } +struct MovieTrackingTrack *tracking_find_slidable_track_in_proximity(struct bContext *C, + const float co[2]) +{ + return tracking_marker_check_slide(C, co, NULL, NULL, NULL); +} + static void *slide_marker_customdata(bContext *C, const wmEvent *event) { SpaceClip *sc = CTX_wm_space_clip(C); @@ -672,7 +651,8 @@ static void *slide_marker_customdata(bContext *C, const wmEvent *event) float co[2]; void *customdata = NULL; int framenr = ED_space_clip_get_clip_frame_number(sc); - int area, action, corner; + eSlideAction action; + int area, corner; ED_space_clip_get_size(sc, &width, &height); @@ -682,7 +662,7 @@ static void *slide_marker_customdata(bContext *C, const wmEvent *event) ED_clip_mouse_pos(sc, region, event->mval, co); - track = tracking_marker_check_slide(C, event, &area, &action, &corner); + track = tracking_marker_check_slide(C, co, &area, &action, &corner); if (track != NULL) { MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); customdata = create_slide_marker_data( @@ -718,14 +698,12 @@ static int slide_marker_invoke(bContext *C, wmOperator *op, const wmEvent *event static void cancel_mouse_slide(SlideMarkerData *data) { - MovieTrackingTrack *track = data->track; MovieTrackingMarker *marker = data->marker; memcpy(marker->pattern_corners, data->old_corners, sizeof(marker->pattern_corners)); copy_v2_v2(marker->search_min, data->old_search_min); copy_v2_v2(marker->search_max, data->old_search_max); copy_v2_v2(marker->pos, data->old_pos); - copy_v2_v2(track->offset, data->old_offset); if (data->old_markers != NULL) { for (int a = 0; a < data->track->markersnr; a++) { @@ -765,7 +743,6 @@ static void free_slide_data(SlideMarkerData *data) static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event) { SpaceClip *sc = CTX_wm_space_clip(C); - ARegion *region = CTX_wm_region(C); SlideMarkerData *data = (SlideMarkerData *)op->customdata; float dx, dy, mdelta[2]; @@ -804,110 +781,66 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (data->area == TRACK_AREA_POINT) { - if (data->action == SLIDE_ACTION_OFFSET) { - data->offset[0] = data->old_offset[0] + dx; - data->offset[1] = data->old_offset[1] + dy; - } - else { - data->pos[0] = data->old_pos[0] + dx; - data->pos[1] = data->old_pos[1] + dy; - } + data->pos[0] += dx; + data->pos[1] += dy; WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); DEG_id_tag_update(&sc->clip->id, 0); } else if (data->area == TRACK_AREA_PAT) { - if (data->action == SLIDE_ACTION_SIZE) { - float start[2], end[2]; - float scale; - - ED_clip_point_stable_pos(sc, region, data->mval[0], data->mval[1], &start[0], &start[1]); - - sub_v2_v2(start, data->old_pos); - - if (len_squared_v2(start) != 0.0f) { - float mval[2]; - - if (data->accurate) { - mval[0] = data->mval[0] + (event->mval[0] - data->mval[0]) / 5.0f; - mval[1] = data->mval[1] + (event->mval[1] - data->mval[1]) / 5.0f; - } - else { - mval[0] = event->mval[0]; - mval[1] = event->mval[1]; - } + if (data->action == SLIDE_ACTION_POS) { + float prev_pos[2]; + copy_v2_v2(prev_pos, data->pos); - ED_clip_point_stable_pos(sc, region, mval[0], mval[1], &end[0], &end[1]); - - sub_v2_v2(end, data->old_pos); - scale = len_v2(end) / len_v2(start); - - if (scale > 0.0f) { - for (int a = 0; a < 4; a++) { - mul_v2_v2fl(data->corners[a], data->old_corners[a], scale); - } - } - } - - BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM); - } - else if (data->action == SLIDE_ACTION_OFFSET) { - const float d[2] = {dx, dy}; - for (int a = 0; a < data->track->markersnr; a++) { - add_v2_v2v2(data->track->markers[a].pos, data->old_markers[a], d); - } - sub_v2_v2v2(data->offset, data->old_offset, d); - } - else if (data->action == SLIDE_ACTION_POS) { - float spos[2]; - - copy_v2_v2(spos, data->pos); - - data->pos[0] = data->spos[0] + dx; - data->pos[1] = data->spos[1] + dy; + data->pos[0] += dx; + data->pos[1] += dy; if (!slide_check_corners(data->corners)) { - copy_v2_v2(data->pos, spos); + copy_v2_v2(data->pos, prev_pos); } - /* Currently only patterns are allowed to have such - * combination of event and data. - */ - BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM); + /* Allow pattern to be arbitrary size and resize search area if needed. */ + BKE_tracking_marker_clamp_search_size(data->marker); } else if (data->action == SLIDE_ACTION_TILT_SIZE) { - const float mouse_delta[2] = {dx, dy}; - - /* Vector which connects marker position with tilt/scale sliding area before sliding - * began. */ - float start[2]; - sub_v2_v2v2(start, data->spos, data->old_pos); - start[0] *= data->width; - start[1] *= data->height; - - /* Vector which connects marker position with tilt/scale sliding area with the sliding - * delta applied. */ - float end[2]; - add_v2_v2v2(end, data->spos, mouse_delta); - sub_v2_v2(end, data->old_pos); - end[0] *= data->width; - end[1] *= data->height; + const float delta[2] = {dx, dy}; + + /* Slider position relative to the marker position using current state of pattern + * corners. */ + float slider[2]; + slide_marker_tilt_slider_relative(data->corners, slider); + + /* Vector which connects marker position with the slider state at the current corners + * state. + * The coordinate is in the pixel space. */ + float start_px[2]; + copy_v2_v2(start_px, slider); + start_px[0] *= data->width; + start_px[1] *= data->height; + + /* Vector which connects marker position with the slider state with the new mouse delta + * taken into account. + * The coordinate is in the pixel space. */ + float end_px[2]; + add_v2_v2v2(end_px, slider, delta); + end_px[0] *= data->width; + end_px[1] *= data->height; float scale = 1.0f; - if (len_squared_v2(start) != 0.0f) { - scale = len_v2(end) / len_v2(start); + if (len_squared_v2(start_px) != 0.0f) { + scale = len_v2(end_px) / len_v2(start_px); if (scale < 0.0f) { scale = 0.0; } } - const float angle = -angle_signed_v2v2(start, end); + const float angle = -angle_signed_v2v2(start_px, end_px); for (int a = 0; a < 4; a++) { float vec[2]; - mul_v2_v2fl(data->corners[a], data->old_corners[a], scale); + mul_v2_fl(data->corners[a], scale); copy_v2_v2(vec, data->corners[a]); vec[0] *= data->width; @@ -917,30 +850,32 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event) data->corners[a][1] = (vec[1] * cosf(angle) + vec[0] * sinf(angle)) / data->height; } - BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM); + BKE_tracking_marker_clamp_search_size(data->marker); } } else if (data->area == TRACK_AREA_SEARCH) { if (data->action == SLIDE_ACTION_SIZE) { - data->min[0] = data->old_search_min[0] - dx; - data->max[0] = data->old_search_max[0] + dx; + data->min[0] -= dx; + data->min[1] += dy; - data->min[1] = data->old_search_min[1] + dy; - data->max[1] = data->old_search_max[1] - dy; + data->max[0] += dx; + data->max[1] -= dy; - BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_DIM); - } - else if (data->area == TRACK_AREA_SEARCH) { - const float d[2] = {dx, dy}; - add_v2_v2v2(data->min, data->old_search_min, d); - add_v2_v2v2(data->max, data->old_search_max, d); + BKE_tracking_marker_clamp_search_size(data->marker); } + else if (data->action == SLIDE_ACTION_OFFSET) { + const float delta[2] = {dx, dy}; + add_v2_v2(data->min, delta); + add_v2_v2(data->max, delta); - BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_POS); + BKE_tracking_marker_clamp_search_position(data->marker); + } } data->marker->flag &= ~MARKER_TRACKED; + copy_v2_v2_int(data->mval, event->mval); + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL); break; @@ -1012,9 +947,9 @@ static int clear_track_path_exec(bContext *C, wmOperator *op) MovieClip *clip = ED_space_clip_get_clip(sc); MovieTracking *tracking = &clip->tracking; ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); - int action = RNA_enum_get(op->ptr, "action"); + const eTrackClearAction action = RNA_enum_get(op->ptr, "action"); const bool clear_active = RNA_boolean_get(op->ptr, "clear_active"); - int framenr = ED_space_clip_get_clip_frame_number(sc); + const int framenr = ED_space_clip_get_clip_frame_number(sc); if (clear_active) { MovieTrackingTrack *track = BKE_tracking_track_get_active(tracking); diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c index ad604fe59bc..bbfbbd2cc58 100644 --- a/source/blender/editors/space_clip/tracking_select.c +++ b/source/blender/editors/space_clip/tracking_select.c @@ -273,7 +273,18 @@ void ed_tracking_deselect_all_plane_tracks(ListBase *plane_tracks_base) } } -static int mouse_select(bContext *C, const float co[2], const bool extend, const bool deselect_all) +static bool select_poll(bContext *C) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + + if (sc) { + return sc->clip && sc->view == SC_VIEW_CLIP; + } + + return false; +} + +static int select_exec(bContext *C, wmOperator *op) { SpaceClip *sc = CTX_wm_space_clip(C); MovieClip *clip = ED_space_clip_get_clip(sc); @@ -281,12 +292,35 @@ static int mouse_select(bContext *C, const float co[2], const bool extend, const ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking); MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking); - MovieTrackingTrack *track; - MovieTrackingPlaneTrack *plane_track; + const bool extend = RNA_boolean_get(op->ptr, "extend"); + const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); + + float co[2]; + RNA_float_get_array(op->ptr, "location", co); + + /* Special code which allows to slide a marker which belongs to currently selected but not yet + * active track. If such track is found activate it and return pass-though so that marker slide + * operator can be used immediately after. + * This logic makes it convenient to slide markers when left mouse selection is used. */ + if (!extend) { + MovieTrackingTrack *track = tracking_find_slidable_track_in_proximity(C, co); + if (track != NULL) { + MovieClip *clip_iter = ED_space_clip_get_clip(sc); + + clip_iter->tracking.act_track = track; + + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + DEG_id_tag_update(&clip_iter->id, ID_RECALC_SELECT); + + return OPERATOR_PASS_THROUGH; + } + } + float distance_to_track, distance_to_plane_track; - track = find_nearest_track(sc, tracksbase, co, &distance_to_track); - plane_track = find_nearest_plane_track(sc, plane_tracks_base, co, &distance_to_plane_track); + MovieTrackingTrack *track = find_nearest_track(sc, tracksbase, co, &distance_to_track); + MovieTrackingPlaneTrack *plane_track = find_nearest_plane_track( + sc, plane_tracks_base, co, &distance_to_plane_track); ClipViewLockState lock_state; ED_clip_view_lock_state_store(C, &lock_state); @@ -375,51 +409,12 @@ static int mouse_select(bContext *C, const float co[2], const bool extend, const return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; } -static bool select_poll(bContext *C) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - - if (sc) { - return sc->clip && sc->view == SC_VIEW_CLIP; - } - - return false; -} - -static int select_exec(bContext *C, wmOperator *op) -{ - float co[2]; - - RNA_float_get_array(op->ptr, "location", co); - const bool extend = RNA_boolean_get(op->ptr, "extend"); - const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - - return mouse_select(C, co, extend, deselect_all); -} - static int select_invoke(bContext *C, wmOperator *op, const wmEvent *event) { SpaceClip *sc = CTX_wm_space_clip(C); ARegion *region = CTX_wm_region(C); float co[2]; - const bool extend = RNA_boolean_get(op->ptr, "extend"); - - if (!extend) { - MovieTrackingTrack *track = tracking_marker_check_slide(C, event, NULL, NULL, NULL); - - if (track) { - MovieClip *clip = ED_space_clip_get_clip(sc); - - clip->tracking.act_track = track; - - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - DEG_id_tag_update(&clip->id, ID_RECALC_SELECT); - - return OPERATOR_PASS_THROUGH; - } - } - ED_clip_mouse_pos(sc, region, event->mval, co); RNA_float_set_array(op->ptr, "location", co); diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 59ecef7d4c6..cfbbc1a1b45 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -336,7 +336,7 @@ enum { }; typedef struct FileListEntryPreview { - char path[FILE_MAX]; + char filepath[FILE_MAX]; uint flags; int index; int attributes; /* from FileDirEntry. */ @@ -1636,13 +1636,13 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat source = THB_SOURCE_FONT; } - IMB_thumb_path_lock(preview->path); + IMB_thumb_path_lock(preview->filepath); /* Always generate biggest preview size for now, it's simpler and avoids having to re-generate * in case user switch to a bigger preview size. Do not create preview when file is offline. */ ImBuf *imbuf = (preview->attributes & FILE_ATTR_OFFLINE) ? - IMB_thumb_read(preview->path, THB_LARGE) : - IMB_thumb_manage(preview->path, THB_LARGE, source); - IMB_thumb_path_unlock(preview->path); + IMB_thumb_read(preview->filepath, THB_LARGE) : + IMB_thumb_manage(preview->filepath, THB_LARGE, source); + IMB_thumb_path_unlock(preview->filepath); if (imbuf) { preview->icon_id = BKE_icon_imbuf_create(imbuf); } @@ -1753,7 +1753,7 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry if (preview_in_memory) { /* TODO(mano-wii): No need to use the thread API here. */ BLI_assert(BKE_previewimg_is_finished(preview_in_memory, ICON_SIZE_PREVIEW)); - preview->path[0] = '\0'; + preview->filepath[0] = '\0'; ImBuf *imbuf = BKE_previewimg_to_imbuf(preview_in_memory, ICON_SIZE_PREVIEW); if (imbuf) { preview->icon_id = BKE_icon_imbuf_create(imbuf); @@ -1762,13 +1762,13 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry } else { if (entry->redirection_path) { - BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR); + BLI_strncpy(preview->filepath, entry->redirection_path, FILE_MAXDIR); } else { BLI_join_dirfile( - preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath); + preview->filepath, sizeof(preview->filepath), filelist->filelist.root, entry->relpath); } - // printf("%s: %d - %s\n", __func__, preview->index, preview->path); + // printf("%s: %d - %s\n", __func__, preview->index, preview->filepath); FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata), __func__); @@ -2667,7 +2667,7 @@ bool filelist_cache_previews_update(FileList *filelist) * we do not want to cache it again here. */ entry = filelist_file_ex(filelist, preview->index, false); - // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); + // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->filepath, preview->img); if (entry) { if (preview->icon_id) { diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 011506368ee..ce36e3e4e4f 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -1364,3 +1364,21 @@ ScrArea *ED_fileselect_handler_area_find(const wmWindow *win, const wmOperator * return NULL; } + +ScrArea *ED_fileselect_handler_area_find_any_with_op(const wmWindow *win) +{ + const bScreen *screen = WM_window_get_active_screen(win); + + ED_screen_areas_iter (win, screen, area) { + if (area->spacetype != SPACE_FILE) { + continue; + } + + const SpaceFile *sfile = area->spacedata.first; + if (sfile->op) { + return area; + } + } + + return NULL; +} diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index 65354591034..10cde239987 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -519,13 +519,13 @@ void fsmenu_remove_entry(struct FSMenu *fsmenu, FSMenuCategory category, int idx } } -void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename) +void fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath) { FSMenuEntry *fsm_iter = NULL; char fsm_name[FILE_MAX]; int nwritten = 0; - FILE *fp = BLI_fopen(filename, "w"); + FILE *fp = BLI_fopen(filepath, "w"); if (!fp) { return; } @@ -556,14 +556,14 @@ void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename) fclose(fp); } -void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filename) +void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filepath) { char line[FILE_MAXDIR]; char name[FILE_MAXFILE]; FSMenuCategory category = FS_CATEGORY_BOOKMARKS; FILE *fp; - fp = BLI_fopen(filename, "r"); + fp = BLI_fopen(filepath, "r"); if (!fp) { return; } diff --git a/source/blender/editors/space_file/fsmenu.h b/source/blender/editors/space_file/fsmenu.h index 861c37ecaa1..6e980a326fc 100644 --- a/source/blender/editors/space_file/fsmenu.h +++ b/source/blender/editors/space_file/fsmenu.h @@ -38,10 +38,10 @@ short fsmenu_can_save(struct FSMenu *fsmenu, enum FSMenuCategory category, int i void fsmenu_remove_entry(struct FSMenu *fsmenu, enum FSMenuCategory category, int idx); /** saves the 'bookmarks' to the specified file */ -void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename); +void fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath); /** reads the 'bookmarks' from the specified file */ -void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filename); +void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filepath); /** adds system specific directories */ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index aa77aab2283..336331e44e7 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1680,184 +1680,28 @@ void IMAGE_OT_replace(wmOperatorType *ot) typedef struct ImageSaveData { ImageUser *iuser; Image *image; - ImageFormatData im_format; + ImageSaveOptions opts; } ImageSaveData; -static char imtype_best_depth(ImBuf *ibuf, const char imtype) +static void image_save_options_from_op(Main *bmain, ImageSaveOptions *opts, wmOperator *op) { - const char depth_ok = BKE_imtype_valid_depths(imtype); - - if (ibuf->rect_float) { - if (depth_ok & R_IMF_CHAN_DEPTH_32) { - return R_IMF_CHAN_DEPTH_32; - } - if (depth_ok & R_IMF_CHAN_DEPTH_24) { - return R_IMF_CHAN_DEPTH_24; - } - if (depth_ok & R_IMF_CHAN_DEPTH_16) { - return R_IMF_CHAN_DEPTH_16; - } - if (depth_ok & R_IMF_CHAN_DEPTH_12) { - return R_IMF_CHAN_DEPTH_12; - } - return R_IMF_CHAN_DEPTH_8; - } - - if (depth_ok & R_IMF_CHAN_DEPTH_8) { - return R_IMF_CHAN_DEPTH_8; - } - if (depth_ok & R_IMF_CHAN_DEPTH_12) { - return R_IMF_CHAN_DEPTH_12; - } - if (depth_ok & R_IMF_CHAN_DEPTH_16) { - return R_IMF_CHAN_DEPTH_16; - } - if (depth_ok & R_IMF_CHAN_DEPTH_24) { - return R_IMF_CHAN_DEPTH_24; - } - if (depth_ok & R_IMF_CHAN_DEPTH_32) { - return R_IMF_CHAN_DEPTH_32; - } - return R_IMF_CHAN_DEPTH_8; /* fallback, should not get here */ -} - -static int image_save_options_init(Main *bmain, - ImageSaveOptions *opts, - Image *ima, - ImageUser *iuser, - const bool guess_path, - const bool save_as_render) -{ - void *lock; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock); - - if (ibuf) { - Scene *scene = opts->scene; - bool is_depth_set = false; - - if (ELEM(ima->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { - /* imtype */ - BKE_image_format_init_for_write(&opts->im_format, scene, NULL); - is_depth_set = true; - if (!BKE_image_is_multiview(ima)) { - /* In case multiview is disabled, - * render settings would be invalid for render result in this area. */ - opts->im_format.stereo3d_format = *ima->stereo3d_format; - opts->im_format.views_format = ima->views_format; - } - } - else { - if (ima->source == IMA_SRC_GENERATED) { - opts->im_format.imtype = R_IMF_IMTYPE_PNG; - opts->im_format.compress = ibuf->foptions.quality; - opts->im_format.planes = ibuf->planes; - } - else { - BKE_image_format_from_imbuf(&opts->im_format, ibuf); - } - - /* use the multiview image settings as the default */ - opts->im_format.stereo3d_format = *ima->stereo3d_format; - opts->im_format.views_format = ima->views_format; - - BKE_image_format_color_management_copy_from_scene(&opts->im_format, scene); - } - - opts->im_format.color_management = R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE; - - if (ima->source == IMA_SRC_TILED) { - BLI_strncpy(opts->filepath, ima->filepath, sizeof(opts->filepath)); - BLI_path_abs(opts->filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); - } - else { - BLI_strncpy(opts->filepath, ibuf->name, sizeof(opts->filepath)); - } - - /* sanitize all settings */ - - /* unlikely but just in case */ - if (ELEM(opts->im_format.planes, R_IMF_PLANES_BW, R_IMF_PLANES_RGB, R_IMF_PLANES_RGBA) == 0) { - opts->im_format.planes = R_IMF_PLANES_RGBA; - } - - /* depth, account for float buffer and format support */ - if (is_depth_set == false) { - opts->im_format.depth = imtype_best_depth(ibuf, opts->im_format.imtype); - } - - /* some formats don't use quality so fallback to scenes quality */ - if (opts->im_format.quality == 0) { - opts->im_format.quality = scene->r.im_format.quality; - } - - /* check for empty path */ - if (guess_path && opts->filepath[0] == 0) { - const bool is_prev_save = !STREQ(G.ima, "//"); - if (save_as_render) { - if (is_prev_save) { - BLI_strncpy(opts->filepath, G.ima, sizeof(opts->filepath)); - } - else { - BLI_strncpy(opts->filepath, "//untitled", sizeof(opts->filepath)); - BLI_path_abs(opts->filepath, BKE_main_blendfile_path(bmain)); - } - } - else { - BLI_snprintf(opts->filepath, sizeof(opts->filepath), "//%s", ima->id.name + 2); - BLI_path_make_safe(opts->filepath); - BLI_path_abs(opts->filepath, is_prev_save ? G.ima : BKE_main_blendfile_path(bmain)); - } - - /* append UDIM marker if not present */ - if (ima->source == IMA_SRC_TILED && strstr(opts->filepath, "<UDIM>") == NULL) { - int len = strlen(opts->filepath); - STR_CONCAT(opts->filepath, len, ".<UDIM>"); - } - } - } - - BKE_image_release_ibuf(ima, ibuf, lock); - - return (ibuf != NULL); -} - -static void image_save_options_from_op(Main *bmain, - ImageSaveOptions *opts, - wmOperator *op, - ImageFormatData *imf) -{ - if (imf) { - BKE_image_format_free(&opts->im_format); - BKE_image_format_copy(&opts->im_format, imf); - } - if (RNA_struct_property_is_set(op->ptr, "filepath")) { RNA_string_get(op->ptr, "filepath", opts->filepath); BLI_path_abs(opts->filepath, BKE_main_blendfile_path(bmain)); } -} - -static void image_save_options_to_op(ImageSaveOptions *opts, wmOperator *op) -{ - if (op->customdata) { - ImageSaveData *isd = op->customdata; - BKE_image_format_free(&isd->im_format); - BKE_image_format_copy(&isd->im_format, &opts->im_format); - } - - RNA_string_set(op->ptr, "filepath", opts->filepath); -} -static bool save_image_op( - Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, ImageSaveOptions *opts) -{ opts->relative = (RNA_struct_find_property(op->ptr, "relative_path") && RNA_boolean_get(op->ptr, "relative_path")); opts->save_copy = (RNA_struct_find_property(op->ptr, "copy") && RNA_boolean_get(op->ptr, "copy")); opts->save_as_render = (RNA_struct_find_property(op->ptr, "save_as_render") && RNA_boolean_get(op->ptr, "save_as_render")); + opts->do_newpath = true; +} +static bool save_image_op( + Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, ImageSaveOptions *opts) +{ WM_cursor_wait(true); bool ok = BKE_image_save(op->reports, bmain, ima, iuser, opts); @@ -1872,11 +1716,54 @@ static bool save_image_op( return ok; } +static ImageSaveData *image_save_as_init(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Image *image = image_from_context(C); + ImageUser *iuser = image_user_from_context(C); + Scene *scene = CTX_data_scene(C); + + ImageSaveData *isd = MEM_callocN(sizeof(*isd), __func__); + isd->image = image; + isd->iuser = iuser; + + if (!BKE_image_save_options_init(&isd->opts, bmain, scene, image, iuser, true)) { + BKE_image_save_options_free(&isd->opts); + MEM_freeN(isd); + return NULL; + } + + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + RNA_string_set(op->ptr, "filepath", isd->opts.filepath); + } + + /* Enable save_copy by default for render results. */ + if (ELEM(image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE) && + !RNA_struct_property_is_set(op->ptr, "copy")) { + RNA_boolean_set(op->ptr, "copy", true); + } + + if (!RNA_struct_property_is_set(op->ptr, "save_as_render")) { + RNA_boolean_set(op->ptr, "save_as_render", isd->opts.save_as_render); + } + + /* Show multiview save options only if image has multiviews. */ + PropertyRNA *prop; + prop = RNA_struct_find_property(op->ptr, "show_multiview"); + RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(image)); + prop = RNA_struct_find_property(op->ptr, "use_multiview"); + RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(image)); + + op->customdata = isd; + + return isd; +} + static void image_save_as_free(wmOperator *op) { if (op->customdata) { ImageSaveData *isd = op->customdata; - BKE_image_format_free(&isd->im_format); + BKE_image_save_options_free(&isd->opts); MEM_freeN(op->customdata); op->customdata = NULL; @@ -1886,96 +1773,55 @@ static void image_save_as_free(wmOperator *op) static int image_save_as_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ImageSaveOptions opts; + ImageSaveData *isd; - Image *image = NULL; - ImageUser *iuser = NULL; - ImageFormatData *imf = NULL; if (op->customdata) { - ImageSaveData *isd = op->customdata; - image = isd->image; - iuser = isd->iuser; - imf = &isd->im_format; + isd = op->customdata; } else { - image = image_from_context(C); - iuser = image_user_from_context(C); + isd = image_save_as_init(C, op); + if (isd == NULL) { + return OPERATOR_CANCELLED; + } } - BKE_image_save_options_init(&opts, bmain, scene); - - /* just in case to initialize values, - * these should be set on invoke or by the caller. */ - image_save_options_init(bmain, &opts, image, iuser, false, false); + image_save_options_from_op(bmain, &isd->opts, op); + BKE_image_save_options_update(&isd->opts, isd->image); - image_save_options_from_op(bmain, &opts, op, imf); - opts.do_newpath = true; + save_image_op(bmain, isd->image, isd->iuser, op, &isd->opts); - save_image_op(bmain, image, iuser, op, &opts); - - if (opts.save_copy == false) { - BKE_image_free_packedfiles(image); + if (isd->opts.save_copy == false) { + BKE_image_free_packedfiles(isd->image); } - BKE_image_save_options_free(&opts); - image_save_as_free(op); return OPERATOR_FINISHED; } -static bool image_save_as_check(bContext *UNUSED(C), wmOperator *op) +static bool image_save_as_check(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); ImageSaveData *isd = op->customdata; - return WM_operator_filesel_ensure_ext_imtype(op, &isd->im_format); + + image_save_options_from_op(bmain, &isd->opts, op); + BKE_image_save_options_update(&isd->opts, isd->image); + + return WM_operator_filesel_ensure_ext_imtype(op, &isd->opts.im_format); } static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - Main *bmain = CTX_data_main(C); - Image *ima = image_from_context(C); - ImageUser *iuser = image_user_from_context(C); - Scene *scene = CTX_data_scene(C); - ImageSaveOptions opts; - PropertyRNA *prop; - const bool save_as_render = (ima->source == IMA_SRC_VIEWER); - if (RNA_struct_property_is_set(op->ptr, "filepath")) { return image_save_as_exec(C, op); } - BKE_image_save_options_init(&opts, bmain, scene); - - if (image_save_options_init(bmain, &opts, ima, iuser, true, save_as_render) == 0) { - BKE_image_save_options_free(&opts); + ImageSaveData *isd = image_save_as_init(C, op); + if (isd == NULL) { return OPERATOR_CANCELLED; } - image_save_options_to_op(&opts, op); - /* enable save_copy by default for render results */ - if (ELEM(ima->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE) && - !RNA_struct_property_is_set(op->ptr, "copy")) { - RNA_boolean_set(op->ptr, "copy", true); - } - - RNA_boolean_set(op->ptr, "save_as_render", save_as_render); - - ImageSaveData *isd = MEM_callocN(sizeof(*isd), __func__); - isd->image = ima; - isd->iuser = iuser; - - BKE_image_format_copy(&isd->im_format, &opts.im_format); - op->customdata = isd; - - /* show multiview save options only if image has multiviews */ - prop = RNA_struct_find_property(op->ptr, "show_multiview"); - RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(ima)); - prop = RNA_struct_find_property(op->ptr, "use_multiview"); - RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(ima)); - - image_filesel(C, op, opts.filepath); - BKE_image_save_options_free(&opts); + image_filesel(C, op, isd->opts.filepath); return OPERATOR_RUNNING_MODAL; } @@ -2003,7 +1849,7 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) ImageSaveData *isd = op->customdata; PointerRNA imf_ptr; const bool is_multiview = RNA_boolean_get(op->ptr, "show_multiview"); - const bool use_color_management = RNA_boolean_get(op->ptr, "save_as_render"); + const bool save_as_render = RNA_boolean_get(op->ptr, "save_as_render"); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -2015,8 +1861,15 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) uiItemS(layout); /* image template */ - RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &isd->im_format, &imf_ptr); - uiTemplateImageSettings(layout, &imf_ptr, use_color_management); + RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &isd->opts.im_format, &imf_ptr); + uiTemplateImageSettings(layout, &imf_ptr, save_as_render); + + if (!save_as_render) { + PointerRNA linear_settings_ptr = RNA_pointer_get(&imf_ptr, "linear_colorspace_settings"); + uiLayout *col = uiLayoutColumn(layout, true); + uiItemS(col); + uiItemR(col, &linear_settings_ptr, "name", 0, IFACE_("Color Space"), ICON_NONE); + } /* multiview template */ if (is_multiview) { @@ -2062,16 +1915,19 @@ void IMAGE_OT_save_as(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_boolean(ot->srna, - "save_as_render", - 0, - "Save As Render", - "Apply render part of display transform when saving byte image"); - RNA_def_boolean(ot->srna, - "copy", - 0, - "Copy", - "Create a new image file without modifying the current image in blender"); + PropertyRNA *prop; + prop = RNA_def_boolean(ot->srna, + "save_as_render", + 0, + "Save As Render", + "Apply render part of display transform when saving byte image"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, + "copy", + 0, + "Copy", + "Create a new image file without modifying the current image in blender"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); image_operator_prop_allow_tokens(ot); WM_operator_properties_filesel(ot, @@ -2138,12 +1994,11 @@ static int image_save_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } - BKE_image_save_options_init(&opts, bmain, scene); - if (image_save_options_init(bmain, &opts, image, iuser, false, false) == 0) { + if (!BKE_image_save_options_init(&opts, bmain, scene, image, iuser, false)) { BKE_image_save_options_free(&opts); return OPERATOR_CANCELLED; } - image_save_options_from_op(bmain, &opts, op, NULL); + image_save_options_from_op(bmain, &opts, op); /* Check if file write permission is ok. */ if (BLI_exists(opts.filepath) && !BLI_file_is_writable(opts.filepath)) { @@ -2316,6 +2171,14 @@ static bool image_has_valid_path(Image *ima) return strchr(ima->filepath, '\\') || strchr(ima->filepath, '/'); } +static bool image_should_pack_during_save_all(const Image *ima) +{ + /* Images without a filepath (implied with IMA_SRC_GENERATED) should + * be packed during a save_all operation. */ + return (ima->source == IMA_SRC_GENERATED) || + (ima->source == IMA_SRC_TILED && !BKE_image_has_filepath(ima)); +} + bool ED_image_should_save_modified(const Main *bmain) { ReportList reports; @@ -2339,7 +2202,7 @@ int ED_image_save_all_modified_info(const Main *bmain, ReportList *reports) bool is_format_writable; if (image_should_be_saved(ima, &is_format_writable)) { - if (BKE_image_has_packedfile(ima) || (ima->source == IMA_SRC_GENERATED)) { + if (BKE_image_has_packedfile(ima) || image_should_pack_during_save_all(ima)) { if (!ID_IS_LINKED(ima)) { num_saveable_images++; } @@ -2396,15 +2259,14 @@ bool ED_image_save_all_modified(const bContext *C, ReportList *reports) bool is_format_writable; if (image_should_be_saved(ima, &is_format_writable)) { - if (BKE_image_has_packedfile(ima) || (ima->source == IMA_SRC_GENERATED)) { + if (BKE_image_has_packedfile(ima) || image_should_pack_during_save_all(ima)) { BKE_image_memorypack(ima); } else if (is_format_writable) { if (image_has_valid_path(ima)) { ImageSaveOptions opts; Scene *scene = CTX_data_scene(C); - BKE_image_save_options_init(&opts, bmain, scene); - if (image_save_options_init(bmain, &opts, ima, NULL, false, false)) { + if (!BKE_image_save_options_init(&opts, bmain, scene, ima, NULL, false)) { bool saved_successfully = BKE_image_save(reports, bmain, ima, NULL, &opts); ok = ok && saved_successfully; } @@ -3040,9 +2902,8 @@ static bool image_pack_test(bContext *C, wmOperator *op) return false; } - if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) { - BKE_report( - op->reports, RPT_ERROR, "Packing movies, image sequences or tiled images not supported"); + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { + BKE_report(op->reports, RPT_ERROR, "Packing movies or image sequences not supported"); return false; } @@ -3110,9 +2971,8 @@ static int image_unpack_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) { - BKE_report( - op->reports, RPT_ERROR, "Unpacking movies, image sequences or tiled images not supported"); + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { + BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported"); return OPERATOR_CANCELLED; } @@ -3144,9 +3004,8 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE return OPERATOR_CANCELLED; } - if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) { - BKE_report( - op->reports, RPT_ERROR, "Unpacking movies, image sequences or tiled images not supported"); + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { + BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported"); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c index c3a48abcae1..1fd9fde084b 100644 --- a/source/blender/editors/space_image/image_undo.c +++ b/source/blender/editors/space_image/image_undo.c @@ -1040,7 +1040,7 @@ static ImageUndoStep *image_undo_push_begin(const char *name, int paint_mode) bContext *C = NULL; /* special case, we never read from this. */ UndoStep *us_p = BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_IMAGE); ImageUndoStep *us = (ImageUndoStep *)us_p; - BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D)); + BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D, PAINT_MODE_SCULPT)); us->paint_mode = paint_mode; return us; } diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index 5e4389279eb..f89bfd2a36a 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -107,6 +107,7 @@ bool nla_panel_context(const bContext *C, found = 1; break; } + case ANIMTYPE_NLAACTION: case ANIMTYPE_SCENE: /* Top-Level Widgets doubling up as datablocks */ case ANIMTYPE_OBJECT: case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 8b059b33a9a..40082b08806 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -58,14 +58,12 @@ * --> Most channels are now selection only. */ -static int mouse_nla_channels( - bContext *C, bAnimContext *ac, float x, int channel_index, short selectmode) +static int mouse_nla_channels(bContext *C, bAnimContext *ac, int channel_index, short selectmode) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; - View2D *v2d = &ac->region->v2d; int notifierFlags = 0; /* get the channel that was clicked on */ @@ -203,47 +201,8 @@ static int mouse_nla_channels( } case ANIMTYPE_NLATRACK: { NlaTrack *nlt = (NlaTrack *)ale->data; - AnimData *adt = ale->adt; - short offset; - - /* offset for start of channel (on LHS of channel-list) */ - if (ale->id) { - /* special exception for materials and particles */ - if (ELEM(GS(ale->id->name), ID_MA, ID_PA)) { - offset = 21 + NLACHANNEL_BUTTON_WIDTH; - } - else { - offset = 14; - } - } - else { - offset = 0; - } - if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) { - /* toggle protection (only if there's a toggle there) */ - nlt->flag ^= NLATRACK_PROTECTED; - - /* notifier flags - channel was edited */ - notifierFlags |= (ND_ANIMCHAN | NA_EDITED); - } - else if (x >= (v2d->cur.xmax - 2 * NLACHANNEL_BUTTON_WIDTH)) { - /* toggle mute */ - nlt->flag ^= NLATRACK_MUTED; - - /* notifier flags - channel was edited */ - notifierFlags |= (ND_ANIMCHAN | NA_EDITED); - ale->update |= ANIM_UPDATE_DEPS; - } - else if (x <= ((NLACHANNEL_BUTTON_WIDTH * 2) + offset)) { - /* toggle 'solo' */ - BKE_nlatrack_solo_toggle(adt, nlt); - - /* notifier flags - channel was edited */ - notifierFlags |= (ND_ANIMCHAN | NA_EDITED); - ale->update |= ANIM_UPDATE_DEPS; - } - else if (nlaedit_is_tweakmode_on(ac) == 0) { + if (nlaedit_is_tweakmode_on(ac) == 0) { /* set selection */ if (selectmode == SELECT_INVERT) { /* inverse selection status of this F-Curve only */ @@ -269,61 +228,40 @@ static int mouse_nla_channels( case ANIMTYPE_NLAACTION: { AnimData *adt = BKE_animdata_from_id(ale->id); - /* button region... */ - if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) { - if (nlaedit_is_tweakmode_on(ac) == 0) { - /* 'push-down' action - only usable when not in tweak-mode */ - /* TODO: make this use the operator instead of calling the function directly - * however, calling the operator requires that we supply the args, - * and that works with proper buttons only */ - BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(ale->id)); - } - else { - /* When in tweak-mode, this button becomes the toggle for mapped editing. */ - adt->flag ^= ADT_NLA_EDIT_NOMAP; - } + /* NOTE: rest of NLA-Action name doubles for operating on the AnimData block + * - this is useful when there's no clear divider, and makes more sense in + * the case of users trying to use this to change actions + * - in tweak-mode, clicking here gets us out of tweak-mode, as changing selection + * while in tweak-mode is really evil! + * - we disable "solo" flags too, to make it easier to work with stashed actions + * with less trouble + */ + if (nlaedit_is_tweakmode_on(ac)) { + /* Exit tweak-mode immediately. */ + nlaedit_disable_tweakmode(ac, true); /* changes to NLA-Action occurred */ notifierFlags |= ND_NLA_ACTCHANGE; ale->update |= ANIM_UPDATE_DEPS; } - /* OR rest of name... */ else { - /* NOTE: rest of NLA-Action name doubles for operating on the AnimData block - * - this is useful when there's no clear divider, and makes more sense in - * the case of users trying to use this to change actions - * - in tweak-mode, clicking here gets us out of tweak-mode, as changing selection - * while in tweak-mode is really evil! - * - we disable "solo" flags too, to make it easier to work with stashed actions - * with less trouble - */ - if (nlaedit_is_tweakmode_on(ac)) { - /* Exit tweak-mode immediately. */ - nlaedit_disable_tweakmode(ac, true); - - /* changes to NLA-Action occurred */ - notifierFlags |= ND_NLA_ACTCHANGE; - ale->update |= ANIM_UPDATE_DEPS; + /* select/deselect */ + if (selectmode == SELECT_INVERT) { + /* inverse selection status of this AnimData block only */ + adt->flag ^= ADT_UI_SELECTED; } else { - /* select/deselect */ - if (selectmode == SELECT_INVERT) { - /* inverse selection status of this AnimData block only */ - adt->flag ^= ADT_UI_SELECTED; - } - else { - /* select AnimData block by itself */ - ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR); - adt->flag |= ADT_UI_SELECTED; - } - - /* set active? */ - if (adt->flag & ADT_UI_SELECTED) { - adt->flag |= ADT_UI_ACTIVE; - } + /* select AnimData block by itself */ + ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR); + adt->flag |= ADT_UI_SELECTED; + } - notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); + /* set active? */ + if (adt->flag & ADT_UI_SELECTED) { + adt->flag |= ADT_UI_ACTIVE; } + + notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); } break; } @@ -386,7 +324,7 @@ static int nlachannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmEv &channel_index); /* handle mouse-click in the relevant channel then */ - notifierFlags = mouse_nla_channels(C, &ac, x, channel_index, selectmode); + notifierFlags = mouse_nla_channels(C, &ac, channel_index, selectmode); /* set notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | notifierFlags, NULL); diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 958a9fdfc60..d5507619e0d 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -213,6 +213,11 @@ static void node_buts_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *pt uiItemR(layout, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE); } +static void node_buts_combsep_color(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); +} + NodeResizeDirection node_get_resize_direction(const bNode *node, const int x, const int y) { if (node->type == NODE_FRAME) { @@ -480,6 +485,10 @@ static void node_shader_set_butfunc(bNodeType *ntype) case SH_NODE_MATH: ntype->draw_buttons = node_buts_math; break; + case SH_NODE_COMBINE_COLOR: + case SH_NODE_SEPARATE_COLOR: + ntype->draw_buttons = node_buts_combsep_color; + break; case SH_NODE_TEX_IMAGE: ntype->draw_buttons = node_shader_buts_tex_image; ntype->draw_buttons_ex = node_shader_buts_tex_image_ex; @@ -589,6 +598,19 @@ static void node_composit_buts_ycc(uiLayout *layout, bContext *UNUSED(C), Pointe uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); } +static void node_composit_buts_combsep_color(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)node->storage; + + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); + if (storage->mode == CMP_NODE_COMBSEP_COLOR_YCC) { + uiItemR(layout, ptr, "ycc_mode", DEFAULT_FLAGS, "", ICON_NONE); + } +} + static void node_composit_backdrop_viewer( SpaceNode *snode, ImBuf *backdrop, bNode *node, int x, int y) { @@ -821,8 +843,12 @@ static void node_composit_set_butfunc(bNodeType *ntype) case CMP_NODE_HUECORRECT: ntype->draw_buttons = node_composit_buts_huecorrect; break; - case CMP_NODE_COMBYCCA: - case CMP_NODE_SEPYCCA: + case CMP_NODE_COMBINE_COLOR: + case CMP_NODE_SEPARATE_COLOR: + ntype->draw_buttons = node_composit_buts_combsep_color; + break; + case CMP_NODE_COMBYCCA_LEGACY: + case CMP_NODE_SEPYCCA_LEGACY: ntype->draw_buttons = node_composit_buts_ycc; break; case CMP_NODE_MASK_BOX: @@ -975,6 +1001,11 @@ static void node_texture_buts_output(uiLayout *layout, bContext *UNUSED(C), Poin uiItemR(layout, ptr, "filepath", DEFAULT_FLAGS, "", ICON_NONE); } +static void node_texture_buts_combsep_color(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); +} + /* only once called */ static void node_texture_set_butfunc(bNodeType *ntype) { @@ -1020,6 +1051,11 @@ static void node_texture_set_butfunc(bNodeType *ntype) case TEX_NODE_OUTPUT: ntype->draw_buttons = node_texture_buts_output; break; + + case TEX_NODE_COMBINE_COLOR: + case TEX_NODE_SEPARATE_COLOR: + ntype->draw_buttons = node_texture_buts_combsep_color; + break; } } } diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc index 4247d5a1fbc..dfc0beb13fc 100644 --- a/source/blender/editors/space_node/node_context_path.cc +++ b/source/blender/editors/space_node/node_context_path.cc @@ -139,7 +139,7 @@ static void get_context_path_node_geometry(const bContext &C, Object *object = CTX_data_active_object(&C); ui::context_path_add_generic(path, RNA_Object, object); ModifierData *modifier = BKE_object_active_modifier(object); - ui::context_path_add_generic(path, RNA_Modifier, modifier, ICON_MODIFIER); + ui::context_path_add_generic(path, RNA_Modifier, modifier, ICON_GEOMETRY_NODES); context_path_add_node_tree_and_node_groups(snode, path); } } diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 9076b17a926..f5048e0cc67 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -895,9 +895,9 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Mesh: %s vertices, %s edges, %s faces"), - to_string(mesh_info.tot_verts).c_str(), - to_string(mesh_info.tot_edges).c_str(), - to_string(mesh_info.tot_faces).c_str()); + to_string(mesh_info.verts_num).c_str(), + to_string(mesh_info.edges_num).c_str(), + to_string(mesh_info.faces_num).c_str()); ss << line << line_end; break; } @@ -908,7 +908,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Point Cloud: %s points"), - to_string(pointcloud_info.tot_points).c_str()); + to_string(pointcloud_info.points_num).c_str()); ss << line << line_end; break; } @@ -918,7 +918,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Curve: %s splines"), - to_string(curve_info.tot_splines).c_str()); + to_string(curve_info.splines_num).c_str()); ss << line << line_end; break; } @@ -928,7 +928,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Instances: %s"), - to_string(instances_info.tot_instances).c_str()); + to_string(instances_info.instances_num).c_str()); ss << line << line_end; break; } diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 2d7972e2291..fb2f1bf3751 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -66,7 +66,9 @@ namespace blender::ed::space_node { #define USE_ESC_COMPO -/* ***************** composite job manager ********************** */ +/* -------------------------------------------------------------------- */ +/** \name Composite Job Manager + * \{ */ enum { COM_RECALC_COMPOSITE = 1, @@ -293,6 +295,12 @@ static void compo_startjob(void *cjv, } // namespace blender::ed::space_node +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Composite Job C API + * \{ */ + void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene *scene_owner) { using namespace blender::ed::space_node; @@ -336,9 +344,13 @@ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene WM_jobs_start(CTX_wm_manager(C), wm_job); } +/** \} */ + namespace blender::ed::space_node { -/* ***************************************** */ +/* -------------------------------------------------------------------- */ +/** \name Composite Poll & Utility Functions + * \{ */ bool composite_node_active(bContext *C) { @@ -388,8 +400,14 @@ static void send_notifiers_after_tree_change(ID *id, bNodeTree *ntree) } } +/** \} */ + } // namespace blender::ed::space_node +/* -------------------------------------------------------------------- */ +/** \name Node Editor Public API Functions + * \{ */ + void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *root_ntree) { if (C != nullptr) { @@ -783,9 +801,13 @@ void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree)) // node_update_nodetree(C, ntree, 0.0f, 0.0f); } +/** \} */ + namespace blender::ed::space_node { -/* ***************** generic operator functions for nodes ***************** */ +/* -------------------------------------------------------------------- */ +/** \name Generic Operator Functions for Nodes + * \{ */ #if 0 /* UNUSED */ @@ -861,7 +883,11 @@ static void edit_node_properties_get( } #endif -/* ************************** Node generic ************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Generic + * \{ */ /* is rct in visible part of node? */ static bNode *visible_node(SpaceNode &snode, const rctf &rct) @@ -874,7 +900,11 @@ static bNode *visible_node(SpaceNode &snode, const rctf &rct) return nullptr; } -/* ********************** size widget operator ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Size Widget Operator + * \{ */ struct NodeSizeWidget { float mxstart, mystart; @@ -1077,7 +1107,11 @@ void NODE_OT_resize(wmOperatorType *ot) ot->flag = OPTYPE_BLOCKING; } -/* ********************** hidden sockets ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Hidden Sockets + * \{ */ bool node_has_hidden_sockets(bNode *node) { @@ -1211,7 +1245,11 @@ bool node_find_indicated_socket(SpaceNode &snode, return false; } -/* ****************** Link Dimming *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Link Dimming + * \{ */ float node_link_dim_factor(const View2D &v2d, const bNodeLink &link) { @@ -1237,7 +1275,11 @@ bool node_link_is_hidden_or_dimmed(const View2D &v2d, const bNodeLink &link) return nodeLinkIsHidden(&link) || node_link_dim_factor(v2d, link) < 0.5f; } -/* ****************** Duplicate *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Duplicate Operator + * \{ */ static void node_duplicate_reparent_recursive(const Map<const bNode *, bNode *> &node_map, bNode *node) @@ -1422,8 +1464,7 @@ void node_select_all(ListBase *lb, int action) } } -/* ******************************** */ -/* XXX some code needing updating to operators. */ +/* XXX: some code needing updating to operators. */ /* goes over all scenes, reads render layers */ static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op)) @@ -1526,7 +1567,11 @@ void NODE_OT_render_changed(wmOperatorType *ot) ot->flag = 0; } -/* ****************** Hide operator *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Hide Operator + * \{ */ static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag) { @@ -1722,7 +1767,11 @@ void NODE_OT_hide_socket_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Mute operator *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Mute Operator + * \{ */ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1758,7 +1807,11 @@ void NODE_OT_mute_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Delete operator ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Delete Operator + * \{ */ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1793,7 +1846,11 @@ void NODE_OT_delete(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Switch View ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Switch View + * \{ */ static bool node_switch_view_poll(bContext *C) { @@ -1837,7 +1894,12 @@ void NODE_OT_switch_view_update(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Delete with reconnect ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Delete with Reconnect Operator + * \{ */ + static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); @@ -1872,7 +1934,11 @@ void NODE_OT_delete_reconnect(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** File Output Add Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node File Output Add Socket Operator + * \{ */ static int node_output_file_add_socket_exec(bContext *C, wmOperator *op) { @@ -1922,7 +1988,11 @@ void NODE_OT_output_file_add_socket(wmOperatorType *ot) ot->srna, "file_path", "Image", MAX_NAME, "File Path", "Subpath of the output file"); } -/* ****************** Multi File Output Remove Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Multi File Output Remove Socket Operator + * \{ */ static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1968,7 +2038,11 @@ void NODE_OT_output_file_remove_active_socket(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Multi File Output Move Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Multi File Output Move Socket Node + * \{ */ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op) { @@ -2040,7 +2114,11 @@ void NODE_OT_output_file_move_active_socket(wmOperatorType *ot) RNA_def_enum(ot->srna, "direction", direction_items, 2, "Direction", ""); } -/* ****************** Copy Node Color ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Copy Node Color Operator + * \{ */ static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2085,7 +2163,11 @@ void NODE_OT_node_copy_color(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Copy to clipboard ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Copy to Clipboard Operator + * \{ */ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2163,7 +2245,11 @@ void NODE_OT_clipboard_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Paste from clipboard ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Paste from Clipboard + * \{ */ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) { @@ -2287,7 +2373,11 @@ void NODE_OT_clipboard_paste(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** Add interface socket operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Add Interface Socket Operator + * \{ */ static bNodeSocket *ntree_get_active_interface_socket(ListBase *lb) { @@ -2357,7 +2447,11 @@ void NODE_OT_tree_socket_add(wmOperatorType *ot) RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); } -/********************** Remove interface socket operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Remove Interface Socket Operator + * \{ */ static int ntree_socket_remove_exec(bContext *C, wmOperator *op) { @@ -2403,7 +2497,11 @@ void NODE_OT_tree_socket_remove(wmOperatorType *ot) RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); } -/********************** Change interface socket type operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Change Interface Socket Type Operator + * \{ */ static int ntree_socket_change_type_exec(bContext *C, wmOperator *op) { @@ -2503,7 +2601,11 @@ void NODE_OT_tree_socket_change_type(wmOperatorType *ot) ot->prop = prop; } -/********************** Move interface socket operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Move Interface Socket Operator + * \{ */ static const EnumPropertyItem move_direction_items[] = { {1, "UP", 0, "Up", ""}, @@ -2577,7 +2679,11 @@ void NODE_OT_tree_socket_move(wmOperatorType *ot) RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); } -/* ********************** Shader Script Update ******************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Shader Script Update + * \{ */ static bool node_shader_script_update_poll(bContext *C) { @@ -2722,7 +2828,11 @@ void NODE_OT_shader_script_update(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ********************** Viewer border ******************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Viewer Border + * \{ */ static void viewer_border_corner_to_backdrop(SpaceNode *snode, ARegion *region, @@ -2844,7 +2954,11 @@ void NODE_OT_clear_viewer_border(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Cryptomatte Add Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cryptomatte Add Socket + * \{ */ static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2888,7 +3002,11 @@ void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Cryptomatte Remove Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cryptomatte Remove Socket + * \{ */ static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2933,4 +3051,7 @@ void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +/** \} */ + } // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 6d7348bfffc..924537d0e8a 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -31,7 +31,9 @@ struct wmKeyConfig; struct wmWindow; /* Outside of blender namespace to avoid Python documentation build error with `ctypes`. */ +extern "C" { extern const char *node_context_dir[]; +}; namespace blender::ed::space_node { @@ -74,13 +76,17 @@ struct SpaceNode_Runtime { /** Mouse position for drawing socket-less links and adding nodes. */ float2 cursor; - /* Indicates that the compositing tree in the space needs to be re-evaluated using the + /** + * Indicates that the compositing tree in the space needs to be re-evaluated using the * auto-compositing pipeline. - * Takes priority over the regular compsiting. */ + * Takes priority over the regular compositing. + */ bool recalc_auto_compositing; - /* Indicates that the compositing int the space tree needs to be re-evaluated using - * regular compositing pipeline. */ + /** + * Indicates that the compositing int the space tree needs to be re-evaluated using + * regular compositing pipeline. + */ bool recalc_regular_compositing; /** Temporary data for modal linking operator. */ @@ -100,7 +106,7 @@ enum NodeResizeDirection { }; ENUM_OPERATORS(NodeResizeDirection, NODE_RESIZE_LEFT); -/* Nodes draw without dpi - the view zoom is flexible. */ +/* Nodes draw without DPI - the view zoom is flexible. */ #define HIDDEN_RAD (0.75f * U.widget_unit) #define BASIS_RAD (0.2f * U.widget_unit) #define NODE_DYS (U.widget_unit / 2) diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index 005dbc1eb10..9d73156edab 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -716,7 +716,7 @@ void NODE_OT_select(wmOperatorType *ot) prop = RNA_def_int_vector(ot->srna, "location", 2, - NULL, + nullptr, INT_MIN, INT_MAX, "Location", diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index 296cd1ff133..3963186f73b 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -327,8 +327,9 @@ static bool any_node_uses_id(const bNodeTree *ntree, const ID *id) /** * Tag the space to recalculate the compositing tree using auto-compositing pipeline. * - * Will check the space to be using a compsiting tree, and check whether auto-compositing - * is enabled. If the checks do not pass then the function has no affect. */ + * Will check the space to be using a compositing tree, and check whether auto-compositing + * is enabled. If the checks do not pass then the function has no affect. + */ static void node_area_tag_recalc_auto_compositing(SpaceNode *snode, ScrArea *area) { if (!ED_node_is_compositor(snode)) { @@ -347,7 +348,8 @@ static void node_area_tag_recalc_auto_compositing(SpaceNode *snode, ScrArea *are * For all node trees this will do `snode_set_context()` which takes care of setting an active * tree. This will be done in the area refresh callback. * - * For compositor tree this will additionally start of the compositor job. */ + * For compositor tree this will additionally start of the compositor job. + */ static void node_area_tag_tree_recalc(SpaceNode *snode, ScrArea *area) { if (ED_node_is_compositor(snode)) { @@ -824,8 +826,10 @@ static void node_region_listener(const wmRegionListenerParams *params) } // namespace blender::ed::space_node /* Outside of blender namespace to avoid Python documentation build error with `ctypes`. */ +extern "C" { const char *node_context_dir[] = { "selected_nodes", "active_node", "light", "material", "world", nullptr}; +}; namespace blender::ed::space_node { diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index fae0e4be2a8..59f6bd85d59 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -38,8 +38,8 @@ set(SRC tree/tree_display_data.cc tree/tree_display_libraries.cc tree/tree_display_orphaned.cc - tree/tree_display_override_library_properties.cc tree/tree_display_override_library_hierarchies.cc + tree/tree_display_override_library_properties.cc tree/tree_display_scenes.cc tree/tree_display_sequencer.cc tree/tree_display_view_layer.cc diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc index 88640210ea3..a22ce9d3d24 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.cc +++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc @@ -170,7 +170,8 @@ static TreeElement *outliner_drop_insert_find(bContext *C, *r_insert_type = TE_INSERT_BEFORE; return first; } - BLI_assert(0); + + BLI_assert_unreachable(); return nullptr; } diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index 36e21cf51a5..d165e98d7d4 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -38,6 +38,7 @@ #include "BKE_library.h" #include "BKE_main.h" #include "BKE_modifier.h" +#include "BKE_node.h" #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_report.h" @@ -1817,7 +1818,7 @@ static void outliner_draw_overrides_rna_buts(uiBlock *block, te->ys + pad_y, item_max_width, item_height, - NULL, + nullptr, 0.0f, 0.0f, 0.0f, @@ -2524,6 +2525,11 @@ static BIFIconID tree_element_get_icon_from_id(const ID *id) return ICON_WORKSPACE; case ID_MSK: return ICON_MOD_MASK; + case ID_NT: { + const bNodeTree *ntree = (bNodeTree *)id; + const bNodeTreeType *ntreetype = ntree->typeinfo; + return (BIFIconID)ntreetype->ui_icon; + } case ID_MC: return ICON_SEQUENCE; case ID_PC: diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index d6c5901b546..1de45b0ec96 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -334,8 +334,16 @@ static void do_item_rename(ARegion *region, add_textbut = true; } } - else if (te->idcode == ID_LI && ((Library *)tselem->id)->parent) { - BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library"); + else if (te->idcode == ID_LI) { + if (reinterpret_cast<Library *>(tselem->id)->parent) { + BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library"); + } + else { + BKE_report( + reports, + RPT_WARNING, + "Library path is not editable from here anymore, please use Relocate operation instead"); + } } else { add_textbut = true; diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 5da64177e51..cc81d5ed68d 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -231,43 +231,51 @@ static void unlink_material_fn(bContext *UNUSED(C), Material **matar = nullptr; int a, totcol = 0; - if (GS(tsep->id->name) == ID_OB) { - Object *ob = (Object *)tsep->id; - totcol = ob->totcol; - matar = ob->mat; - } - else if (GS(tsep->id->name) == ID_ME) { - Mesh *me = (Mesh *)tsep->id; - totcol = me->totcol; - matar = me->mat; - } - else if (GS(tsep->id->name) == ID_CU_LEGACY) { - Curve *cu = (Curve *)tsep->id; - totcol = cu->totcol; - matar = cu->mat; - } - else if (GS(tsep->id->name) == ID_MB) { - MetaBall *mb = (MetaBall *)tsep->id; - totcol = mb->totcol; - matar = mb->mat; - } - else if (GS(tsep->id->name) == ID_CV) { - Curves *curves = (Curves *)tsep->id; - totcol = curves->totcol; - matar = curves->mat; - } - else if (GS(tsep->id->name) == ID_PT) { - PointCloud *pointcloud = (PointCloud *)tsep->id; - totcol = pointcloud->totcol; - matar = pointcloud->mat; - } - else if (GS(tsep->id->name) == ID_VO) { - Volume *volume = (Volume *)tsep->id; - totcol = volume->totcol; - matar = volume->mat; - } - else { - BLI_assert(0); + switch (GS(tsep->id->name)) { + case ID_OB: { + Object *ob = (Object *)tsep->id; + totcol = ob->totcol; + matar = ob->mat; + break; + } + case ID_ME: { + Mesh *me = (Mesh *)tsep->id; + totcol = me->totcol; + matar = me->mat; + break; + } + case ID_CU_LEGACY: { + Curve *cu = (Curve *)tsep->id; + totcol = cu->totcol; + matar = cu->mat; + break; + } + case ID_MB: { + MetaBall *mb = (MetaBall *)tsep->id; + totcol = mb->totcol; + matar = mb->mat; + break; + } + case ID_CV: { + Curves *curves = (Curves *)tsep->id; + totcol = curves->totcol; + matar = curves->mat; + break; + } + case ID_PT: { + PointCloud *pointcloud = (PointCloud *)tsep->id; + totcol = pointcloud->totcol; + matar = pointcloud->mat; + break; + } + case ID_VO: { + Volume *volume = (Volume *)tsep->id; + totcol = volume->totcol; + matar = volume->mat; + break; + } + default: + BLI_assert_unreachable(); } if (LIKELY(matar != nullptr)) { @@ -492,7 +500,7 @@ static int outliner_scene_operation_exec(bContext *C, wmOperator *op) ED_undo_push(C, "Delete Scene(s)"); } else { - BLI_assert(0); + BLI_assert_unreachable(); return OPERATOR_CANCELLED; } @@ -1726,53 +1734,60 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) event = RNA_enum_get(op->ptr, "type"); - if (event == OL_OP_SELECT) { - Scene *sce = scene; /* To be able to delete, scenes are set... */ - outliner_do_object_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, object_select_fn); - if (scene != sce) { - WM_window_set_active_scene(bmain, C, win, sce); - } + switch (event) { + case OL_OP_SELECT: { + Scene *sce = scene; /* To be able to delete, scenes are set... */ + outliner_do_object_operation( + C, op->reports, scene, space_outliner, &space_outliner->tree, object_select_fn); + /* FIXME: This is most certainly broken, maybe check should rather be + * `if (CTX_data_scene(C) != scene)` ? */ + if (scene != sce) { + WM_window_set_active_scene(bmain, C, win, sce); + } - str = "Select Objects"; - selection_changed = true; - } - else if (event == OL_OP_SELECT_HIERARCHY) { - Scene *sce = scene; /* To be able to delete, scenes are set... */ - outliner_do_object_operation_ex(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - object_select_hierarchy_fn, - nullptr, - false); - if (scene != sce) { - WM_window_set_active_scene(bmain, C, win, sce); - } - str = "Select Object Hierarchy"; - selection_changed = true; - } - else if (event == OL_OP_DESELECT) { - outliner_do_object_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, object_deselect_fn); - str = "Deselect Objects"; - selection_changed = true; - } - else if (event == OL_OP_REMAP) { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_remap_fn, nullptr); - /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth - * trick does not work here). */ - } - else if (event == OL_OP_RENAME) { - outliner_do_object_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn); - str = "Rename Object"; - } - else { - BLI_assert(0); - return OPERATOR_CANCELLED; + str = "Select Objects"; + selection_changed = true; + break; + } + case OL_OP_SELECT_HIERARCHY: { + Scene *sce = scene; /* To be able to delete, scenes are set... */ + outliner_do_object_operation_ex(C, + op->reports, + scene, + space_outliner, + &space_outliner->tree, + object_select_hierarchy_fn, + nullptr, + false); + /* FIXME: This is most certainly broken, maybe check should rather be + * `if (CTX_data_scene(C) != scene)` ? */ + if (scene != sce) { + WM_window_set_active_scene(bmain, C, win, sce); + } + str = "Select Object Hierarchy"; + selection_changed = true; + break; + } + case OL_OP_DESELECT: + outliner_do_object_operation( + C, op->reports, scene, space_outliner, &space_outliner->tree, object_deselect_fn); + str = "Deselect Objects"; + selection_changed = true; + break; + case OL_OP_REMAP: + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, &space_outliner->tree, id_remap_fn, nullptr); + /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth + * trick does not work here). */ + break; + case OL_OP_RENAME: + outliner_do_object_operation( + C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn); + str = "Rename Object"; + break; + default: + BLI_assert_unreachable(); + return OPERATOR_CANCELLED; } if (selection_changed) { @@ -2527,14 +2542,12 @@ void OUTLINER_OT_id_operation(wmOperatorType *ot) enum eOutlinerLibOpTypes { OL_LIB_INVALID = 0, - OL_LIB_RENAME, OL_LIB_DELETE, OL_LIB_RELOCATE, OL_LIB_RELOAD, }; static const EnumPropertyItem outliner_lib_op_type_items[] = { - {OL_LIB_RENAME, "RENAME", 0, "Rename", ""}, {OL_LIB_DELETE, "DELETE", ICON_X, @@ -2566,14 +2579,6 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op) eOutlinerLibOpTypes event = (eOutlinerLibOpTypes)RNA_enum_get(op->ptr, "type"); switch (event) { - case OL_LIB_RENAME: { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn, nullptr); - - WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr); - ED_undo_push(C, "Rename Library"); - break; - } case OL_LIB_DELETE: { outliner_do_libdata_operation( C, op->reports, scene, space_outliner, &space_outliner->tree, id_delete_fn, nullptr); diff --git a/source/blender/editors/space_sequencer/CMakeLists.txt b/source/blender/editors/space_sequencer/CMakeLists.txt index b5355efec7a..44f919ca361 100644 --- a/source/blender/editors/space_sequencer/CMakeLists.txt +++ b/source/blender/editors/space_sequencer/CMakeLists.txt @@ -25,10 +25,10 @@ set(INC set(SRC sequencer_add.c sequencer_buttons.c - sequencer_drag_drop.c - sequencer_draw.c sequencer_channels_draw.c sequencer_channels_edit.c + sequencer_drag_drop.c + sequencer_draw.c sequencer_edit.c sequencer_modifier.c sequencer_ops.c diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 469169cf4cc..b3a3bb4a62b 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -31,6 +31,7 @@ #include "BKE_mask.h" #include "BKE_movieclip.h" #include "BKE_report.h" +#include "BKE_scene.h" #include "BKE_sound.h" #include "IMB_imbuf.h" @@ -54,6 +55,7 @@ #include "SEQ_transform.h" #include "SEQ_utils.h" +#include "ED_scene.h" /* For menu, popup, icons, etc. */ #include "ED_screen.h" #include "ED_sequencer.h" @@ -469,6 +471,125 @@ void SEQUENCER_OT_scene_strip_add(struct wmOperatorType *ot) ot->prop = prop; } +static EnumPropertyItem strip_new_scene_items[] = { + {SCE_COPY_NEW, "NEW", 0, "New", "Add new Strip with a new empty Scene with default settings"}, + {SCE_COPY_EMPTY, + "EMPTY", + 0, + "Copy Settings", + "Add a new Strip, with an empty scene, and copy settings from the current scene"}, + {SCE_COPY_LINK_COLLECTION, + "LINK_COPY", + 0, + "Linked Copy", + "Add a Strip and link in the collections from the current scene (shallow copy)"}, + {SCE_COPY_FULL, + "FULL_COPY", + 0, + "Full Copy", + "Add a Strip and make a full copy of the current scene"}, + {0, NULL, 0, NULL, NULL}, +}; + +static int sequencer_add_scene_strip_new_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + const Editing *ed = SEQ_editing_ensure(scene); + + if (RNA_boolean_get(op->ptr, "replace_sel")) { + ED_sequencer_deselect_all(scene); + } + + SeqLoadData load_data; + load_data_init_from_operator(&load_data, C, op); + + int type = RNA_enum_get(op->ptr, "type"); + Scene *scene_new = ED_scene_sequencer_add(bmain, C, type, false); + if (scene_new == NULL) { + return OPERATOR_CANCELLED; + } + load_data.scene = scene_new; + + Sequence *seq = SEQ_add_scene_strip(scene, ed->seqbasep, &load_data); + seq_load_apply_generic_options(C, op, seq); + + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + + return OPERATOR_FINISHED; +} + +static int sequencer_add_scene_strip_new_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + sequencer_disable_one_time_properties(C, op); + sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_SCENE); + return sequencer_add_scene_strip_new_exec(C, op); +} + +static const EnumPropertyItem *strip_new_sequencer_enum_itemf(bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + EnumPropertyItem *item = NULL; + int totitem = 0; + uint item_index; + + item_index = RNA_enum_from_value(strip_new_scene_items, SCE_COPY_NEW); + RNA_enum_item_add(&item, &totitem, &strip_new_scene_items[item_index]); + + bool has_scene_or_no_context = false; + if (C == NULL) { + /* For documentation generation. */ + has_scene_or_no_context = true; + } + else { + Scene *scene = CTX_data_scene(C); + Sequence *seq = SEQ_select_active_get(scene); + if ((seq && (seq->type == SEQ_TYPE_SCENE) && (seq->scene != NULL))) { + has_scene_or_no_context = true; + } + } + + if (has_scene_or_no_context) { + int values[] = {SCE_COPY_EMPTY, SCE_COPY_LINK_COLLECTION, SCE_COPY_FULL}; + for (int i = 0; i < ARRAY_SIZE(values); i++) { + item_index = RNA_enum_from_value(strip_new_scene_items, values[i]); + RNA_enum_item_add(&item, &totitem, &strip_new_scene_items[item_index]); + } + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + return item; +} + +void SEQUENCER_OT_scene_strip_add_new(struct wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Add Strip with a new Scene"; + ot->idname = "SEQUENCER_OT_scene_strip_add_new"; + ot->description = "Create a new Strip and add a assign a new Scene as source"; + + /* Api callbacks. */ + ot->invoke = sequencer_add_scene_strip_new_invoke; + ot->exec = sequencer_add_scene_strip_new_exec; + ot->poll = ED_operator_sequencer_active_editable; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME); + + ot->prop = RNA_def_enum(ot->srna, "type", strip_new_scene_items, SCE_COPY_NEW, "Type", ""); + RNA_def_enum_funcs(ot->prop, strip_new_sequencer_enum_itemf); + RNA_def_property_flag(ot->prop, PROP_ENUM_NO_TRANSLATE); +} + static int sequencer_add_movieclip_strip_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); diff --git a/source/blender/editors/space_sequencer/sequencer_channels_draw.c b/source/blender/editors/space_sequencer/sequencer_channels_draw.c index a777132e9fc..c11388e8555 100644 --- a/source/blender/editors/space_sequencer/sequencer_channels_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_channels_draw.c @@ -200,7 +200,7 @@ static float text_size_get(const SeqChannelDrawContext *context) return UI_fontstyle_height_max(&style->widget) * 1.5f * context->scale; } -/* Todo: decide what gets priority - label or buttons */ +/* TODO: decide what gets priority - label or buttons. */ static rctf label_rect_init(const SeqChannelDrawContext *context, const int channel_index, const float used_width) @@ -286,7 +286,7 @@ static void draw_channel_labels(const SeqChannelDrawContext *context, } } -/* Todo: different text/buttons alignment */ +/* TODO: different text/buttons alignment. */ static void draw_channel_header(const SeqChannelDrawContext *context, uiBlock *block, const int channel_index) diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 0305ad279a0..677ee0c1683 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -57,6 +57,7 @@ #include "ED_keyframing.h" #include "ED_numinput.h" #include "ED_outliner.h" +#include "ED_scene.h" #include "ED_screen.h" #include "ED_sequencer.h" @@ -1719,11 +1720,26 @@ void SEQUENCER_OT_duplicate(wmOperatorType *ot) /** \name Erase Strips Operator * \{ */ -static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op)) +static void sequencer_delete_strip_data(bContext *C, Sequence *seq) +{ + if (seq->type != SEQ_TYPE_SCENE) { + return; + } + + Main *bmain = CTX_data_main(C); + if (seq->scene) { + if (ED_scene_delete(C, bmain, seq->scene)) { + WM_event_add_notifier(C, NC_SCENE | NA_REMOVED, seq->scene); + } + } +} + +static int sequencer_delete_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ListBase *seqbasep = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + const bool delete_data = RNA_boolean_get(op->ptr, "delete_data"); if (sequencer_view_has_preview_poll(C) && !sequencer_view_preview_only_poll(C)) { return OPERATOR_CANCELLED; @@ -1736,6 +1752,9 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op)) SEQ_ITERATOR_FOREACH (seq, selected_strips) { SEQ_edit_flag_for_removal(scene, seqbasep, seq); + if (delete_data) { + sequencer_delete_strip_data(C, seq); + } } SEQ_edit_remove_flagged_sequences(scene, seqbasep); @@ -1778,6 +1797,14 @@ void SEQUENCER_OT_delete(wmOperatorType *ot) /* Flags. */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties. */ + ot->prop = RNA_def_boolean(ot->srna, + "delete_data", + false, + "Delete Data", + "After removing the Strip, delete the associated data also"); + RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); } /** \} */ @@ -2596,7 +2623,7 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op) } /* Paste animation. - * Note: Only fcurves are copied. Drivers and NLA action strips are not copied. + * NOTE: Only fcurves are copied. Drivers and NLA action strips are not copied. * First backup original curves from scene and move curves from clipboard into scene. This way, * when pasted strips are renamed, pasted fcurves are renamed with them. Finally restore original * curves from backup. @@ -3040,6 +3067,81 @@ void SEQUENCER_OT_change_path(struct wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Change Strip Scene Operator + * \{ */ + +static bool sequencer_strip_change_scene_poll(bContext *C) +{ + Editing *ed = SEQ_editing_get(CTX_data_scene(C)); + if (ed == NULL) { + return false; + } + Sequence *seq = ed->act_seq; + return ((seq != NULL) && (seq->type == SEQ_TYPE_SCENE)); +} +static int sequencer_change_scene_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Scene *scene_seq = BLI_findlink(&bmain->scenes, RNA_enum_get(op->ptr, "scene")); + + if (scene_seq == NULL) { + BKE_report(op->reports, RPT_ERROR, "Scene not found"); + return OPERATOR_CANCELLED; + } + + /* Assign new scene. */ + Sequence *seq = SEQ_select_active_get(scene); + if (seq) { + seq->scene = scene_seq; + /* Do a refresh of the sequencer data. */ + SEQ_relations_invalidate_cache_raw(scene, seq); + DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO | ID_RECALC_SEQUENCER_STRIPS); + DEG_relations_tag_update(bmain); + } + + WM_event_add_notifier(C, NC_SCENE | ND_SCENEBROWSE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + + return OPERATOR_FINISHED; +} + +static int sequencer_change_scene_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!RNA_struct_property_is_set(op->ptr, "scene")) { + return WM_enum_search_invoke(C, op, event); + } + + return sequencer_change_scene_exec(C, op); +} + +void SEQUENCER_OT_change_scene(struct wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* Identifiers. */ + ot->name = "Change Scene"; + ot->idname = "SEQUENCER_OT_change_scene"; + ot->description = "Change Scene assigned to Strip"; + + /* Api callbacks. */ + ot->exec = sequencer_change_scene_exec; + ot->invoke = sequencer_change_scene_invoke; + ot->poll = sequencer_strip_change_scene_poll; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties. */ + prop = RNA_def_enum(ot->srna, "scene", DummyRNA_NULL_items, 0, "Scene", ""); + RNA_def_enum_funcs(prop, RNA_scene_without_active_itemf); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + ot->prop = prop; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Export Subtitles Operator * \{ */ diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 67df065ef35..3307c3fde2f 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -197,6 +197,7 @@ void SEQUENCER_OT_rendersize(struct wmOperatorType *ot); void SEQUENCER_OT_change_effect_input(struct wmOperatorType *ot); void SEQUENCER_OT_change_effect_type(struct wmOperatorType *ot); void SEQUENCER_OT_change_path(struct wmOperatorType *ot); +void SEQUENCER_OT_change_scene(struct wmOperatorType *ot); void SEQUENCER_OT_copy(struct wmOperatorType *ot); void SEQUENCER_OT_paste(struct wmOperatorType *ot); @@ -231,6 +232,7 @@ void SEQUENCER_OT_select_grouped(struct wmOperatorType *ot); /* sequencer_add.c */ void SEQUENCER_OT_scene_strip_add(struct wmOperatorType *ot); +void SEQUENCER_OT_scene_strip_add_new(struct wmOperatorType *ot); void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot); void SEQUENCER_OT_movieclip_strip_add(struct wmOperatorType *ot); void SEQUENCER_OT_mask_strip_add(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index 1aa2991f07a..f7a9bcf41e6 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -58,6 +58,7 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_change_effect_input); WM_operatortype_append(SEQUENCER_OT_change_effect_type); WM_operatortype_append(SEQUENCER_OT_change_path); + WM_operatortype_append(SEQUENCER_OT_change_scene); WM_operatortype_append(SEQUENCER_OT_set_range_to_strips); WM_operatortype_append(SEQUENCER_OT_strip_transform_clear); @@ -81,6 +82,7 @@ void sequencer_operatortypes(void) /* sequencer_add.c */ WM_operatortype_append(SEQUENCER_OT_scene_strip_add); + WM_operatortype_append(SEQUENCER_OT_scene_strip_add_new); WM_operatortype_append(SEQUENCER_OT_movieclip_strip_add); WM_operatortype_append(SEQUENCER_OT_mask_strip_add); WM_operatortype_append(SEQUENCER_OT_movie_strip_add); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 4afa70d9ef6..7f696efabf7 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -64,7 +64,7 @@ std::unique_ptr<ColumnValues> ExtraColumns::get_column_values( void GeometryDataSource::foreach_default_column_ids( FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const { - if (component_->attribute_domain_size(domain_) == 0) { + if (component_->attribute_domain_num(domain_) == 0) { return; } @@ -110,8 +110,8 @@ void GeometryDataSource::foreach_default_column_ids( std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( const SpreadsheetColumnID &column_id) const { - const int domain_size = component_->attribute_domain_size(domain_); - if (domain_size == 0) { + const int domain_num = component_->attribute_domain_num(domain_); + if (domain_num == 0) { return {}; } @@ -129,7 +129,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( Span<InstanceReference> references = instances.references(); return std::make_unique<ColumnValues>( column_id.name, - VArray<InstanceReference>::ForFunc(domain_size, + VArray<InstanceReference>::ForFunc(domain_num, [reference_handles, references](int64_t index) { return references[reference_handles[index]]; })); @@ -137,13 +137,13 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( Span<float4x4> transforms = instances.instance_transforms(); if (STREQ(column_id.name, "Rotation")) { return std::make_unique<ColumnValues>( - column_id.name, VArray<float3>::ForFunc(domain_size, [transforms](int64_t index) { + column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) { return transforms[index].to_euler(); })); } if (STREQ(column_id.name, "Scale")) { return std::make_unique<ColumnValues>( - column_id.name, VArray<float3>::ForFunc(domain_size, [transforms](int64_t index) { + column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) { return transforms[index].scale(); })); } @@ -210,7 +210,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( int GeometryDataSource::tot_rows() const { - return component_->attribute_domain_size(domain_); + return component_->attribute_domain_num(domain_); } /** @@ -256,7 +256,7 @@ IndexMask GeometryDataSource::apply_selection_filter(Vector<int64_t> &indices) c BMesh *bm = mesh_orig->edit_mesh->bm; BM_mesh_elem_table_ensure(bm, BM_VERT); - int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX); + const int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX); if (orig_indices != nullptr) { /* Use CD_ORIGINDEX layer if it exists. */ VArray<bool> selection = mesh_component->attribute_try_adapt_domain<bool>( @@ -524,17 +524,17 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet, std::make_unique<GeometryComponentCacheKey>(component)); const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain; - const int domain_size = component.attribute_domain_size(domain); + const int domain_num = component.attribute_domain_num(domain); for (const auto item : fields_to_show.items()) { StringRef name = item.key; const GField &field = item.value; /* Use the cached evaluated array if it exists, otherwise evaluate the field now. */ GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() { - GArray<> evaluated_array(field.cpp_type(), domain_size); + GArray<> evaluated_array(field.cpp_type(), domain_num); bke::GeometryComponentFieldContext field_context{component, domain}; - fn::FieldEvaluator field_evaluator{field_context, domain_size}; + fn::FieldEvaluator field_evaluator{field_context, domain_num}; field_evaluator.add_with_destination(field, evaluated_array); field_evaluator.evaluate(); return evaluated_array; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc index c4b5228758c..724d0783707 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc @@ -144,7 +144,7 @@ void GeometryDataSetTreeViewItem::build_row(uiLayout &row) /* Using the tree row button instead of a separate right aligned button gives padding * to the right side of the number, which it didn't have with the button. */ char element_count[7]; - BLI_str_format_attribute_domain_size(element_count, *count); + BLI_str_format_decimal_unit(element_count, *count); UI_but_hint_drawstr_set((uiBut *)this->tree_row_button(), element_count); } } @@ -194,7 +194,7 @@ std::optional<int> GeometryDataSetTreeViewItem::count() const } if (const GeometryComponent *component = geometry.get_component_for_read(component_type_)) { - return component->attribute_domain_size(*domain_); + return component->attribute_domain_num(*domain_); } return 0; diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 36eafedbc80..c93ffccd477 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -1603,7 +1603,7 @@ void draw_text_main(SpaceText *st, ARegion *region) return; } - /* dpi controlled line height and font size */ + /* DPI controlled line height and font size. */ st->runtime.lheight_px = (U.widget_unit * st->lheight) / 20; /* don't draw lines below this */ diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 7776119a594..8add6886584 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -6,12 +6,14 @@ */ #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" -#include "BLI_math.h" +#include "BLI_math_vector.h" #include "BKE_DerivedMesh.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_global.h" #include "BKE_object.h" diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index 85d239507ce..1230515a180 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -30,7 +30,7 @@ void ED_view3d_project_float_v2_m4(const ARegion *region, const float co[3], float r_co[2], - float mat[4][4]) + const float mat[4][4]) { float vec4[4]; @@ -52,7 +52,7 @@ void ED_view3d_project_float_v2_m4(const ARegion *region, void ED_view3d_project_float_v3_m4(const ARegion *region, const float co[3], float r_co[3], - float mat[4][4]) + const float mat[4][4]) { float vec4[4]; @@ -312,7 +312,7 @@ float ED_view3d_calc_depth_for_comparison(const RegionView3D *rv3d, const float return -dot_v3v3(rv3d->viewinv[2], co); } -static void view3d_win_to_ray_segment(struct Depsgraph *depsgraph, +static void view3d_win_to_ray_segment(const struct Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const float mval[2], @@ -642,9 +642,9 @@ void ED_view3d_win_to_vector(const ARegion *region, const float mval[2], float r normalize_v3(r_out); } -bool ED_view3d_win_to_segment_clipped(struct Depsgraph *depsgraph, +bool ED_view3d_win_to_segment_clipped(const struct Depsgraph *depsgraph, const ARegion *region, - View3D *v3d, + const View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_end[3], diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index d99063a9b26..efe89621e7b 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -126,6 +126,7 @@ void ED_view3d_viewcontext_init(bContext *C, ViewContext *vc, Depsgraph *depsgra void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact) { vc->obact = obact; + /* See public doc-string for rationale on checking the existing values first. */ if (vc->obedit) { BLI_assert(BKE_object_is_in_editmode(obact)); vc->obedit = obact; diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index e6895c0f4a3..0306bfe70bd 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -106,7 +106,7 @@ void ED_view3d_dist_range_get(const View3D *v3d, float r_dist_range[2]) r_dist_range[1] = v3d->clip_end * 10.0f; } -bool ED_view3d_clip_range_get(Depsgraph *depsgraph, +bool ED_view3d_clip_range_get(const Depsgraph *depsgraph, const View3D *v3d, const RegionView3D *rv3d, float *r_clipsta, diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index dbe67bd0d66..018468dcd03 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -479,6 +479,45 @@ TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTr /** \name UV Coordinates * \{ */ +/** + * Find the correction for the scaling factor when "Constrain to Bounds" is active. + * \param numerator: How far the UV boundary (unit square) is from the origin of the scale. + * \param denominator: How far the AABB is from the origin of the scale. + * \param scale: Scale parameter to update. + */ +static void constrain_scale_to_boundary(const float numerator, + const float denominator, + float *scale) +{ + if (denominator == 0.0f) { + /* The origin of the scale is on the edge of the boundary. */ + if (numerator < 0.0f) { + /* Negative scale will wrap around and put us outside the boundary. */ + *scale = 0.0f; /* Hold at the boundary instead. */ + } + return; /* Nothing else we can do without more info. */ + } + + const float correction = numerator / denominator; + if (correction < 0.0f || !isfinite(correction)) { + /* TODO: Correction is negative or invalid, but we lack context to fix `*scale`. */ + return; + } + + if (denominator < 0.0f) { + /* Scale origin is outside boundary, only make scale bigger. */ + if (*scale < correction) { + *scale = correction; + } + return; + } + + /* Scale origin is inside boundary, the "regular" case, limit maximum scale. */ + if (*scale > correction) { + *scale = correction; + } +} + bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) { bool clipx = true, clipy = true; @@ -517,31 +556,29 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) } if (resize) { - if (min[0] < base_offset[0] && t->center_global[0] > base_offset[0] && - t->center_global[0] < base_offset[0] + (t->aspect[0] * 0.5f)) { - vec[0] *= (t->center_global[0] - base_offset[0]) / (t->center_global[0] - min[0]); - } - else if (max[0] > (base_offset[0] + t->aspect[0]) && - t->center_global[0] < (base_offset[0] + t->aspect[0])) { - vec[0] *= (t->center_global[0] - (base_offset[0] + t->aspect[0])) / - (t->center_global[0] - max[0]); - } - else { - clipx = 0; - } - - if (min[1] < base_offset[1] && t->center_global[1] > base_offset[1] && - t->center_global[1] < base_offset[1] + (t->aspect[1] * 0.5f)) { - vec[1] *= (t->center_global[1] - base_offset[1]) / (t->center_global[1] - min[1]); - } - else if (max[1] > (base_offset[1] + t->aspect[1]) && - t->center_global[1] < (base_offset[1] + t->aspect[1])) { - vec[1] *= (t->center_global[1] - (base_offset[1] + t->aspect[1])) / - (t->center_global[1] - max[1]); - } - else { - clipy = 0; - } + /* Assume no change is required. */ + float scalex = 1.0f; + float scaley = 1.0f; + + /* Update U against the left border. */ + constrain_scale_to_boundary( + t->center_global[0] - base_offset[0], t->center_global[0] - min[0], &scalex); + /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */ + constrain_scale_to_boundary(base_offset[0] + t->aspect[0] - t->center_global[0], + max[0] - t->center_global[0], + &scalex); + + /* Do the same for the V co-ordinate, which is called `y`. */ + constrain_scale_to_boundary( + t->center_global[1] - base_offset[1], t->center_global[1] - min[1], &scaley); + constrain_scale_to_boundary(base_offset[1] + t->aspect[1] - t->center_global[1], + max[1] - t->center_global[1], + &scaley); + + clipx = (scalex != 1.0f); + clipy = (scaley != 1.0f); + vec[0] *= scalex; + vec[1] *= scaley; } else { if (min[0] < base_offset[0]) { diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.c index d5d79bedbf4..8281052c314 100644 --- a/source/blender/editors/transform/transform_convert_node.c +++ b/source/blender/editors/transform/transform_convert_node.c @@ -46,7 +46,7 @@ static void NodeToTransData(TransData *td, TransData2D *td2d, bNode *node, const } /* use top-left corner as the transform origin for nodes */ - /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */ + /* Weirdo - but the node system is a mix of free 2d elements and DPI sensitive UI. */ #ifdef USE_NODE_CENTER td2d->loc[0] = (locx * dpi_fac) + (BLI_rctf_size_x(&node->totr) * +0.5f); td2d->loc[1] = (locy * dpi_fac) + (BLI_rctf_size_y(&node->totr) * -0.5f); @@ -194,7 +194,7 @@ void flushTransNodes(TransInfo *t) loc[1] += 0.5f * BLI_rctf_size_y(&node->totr); #endif - /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */ + /* Weirdo - but the node system is a mix of free 2d elements and DPI sensitive UI. */ loc[0] /= dpi_fac; loc[1] /= dpi_fac; diff --git a/source/blender/editors/transform/transform_convert_tracking.c b/source/blender/editors/transform/transform_convert_tracking.c index bf9f929dd65..d447cd71a40 100644 --- a/source/blender/editors/transform/transform_convert_tracking.c +++ b/source/blender/editors/transform/transform_convert_tracking.c @@ -713,23 +713,23 @@ void recalcData_tracking(TransInfo *t) if (t->mode == TFM_TRANSLATION) { if (TRACK_AREA_SELECTED(track, TRACK_AREA_PAT)) { - BKE_tracking_marker_clamp(marker, CLAMP_PAT_POS); + BKE_tracking_marker_clamp_pattern_position(marker); } if (TRACK_AREA_SELECTED(track, TRACK_AREA_SEARCH)) { - BKE_tracking_marker_clamp(marker, CLAMP_SEARCH_POS); + BKE_tracking_marker_clamp_search_position(marker); } } else if (t->mode == TFM_RESIZE) { if (TRACK_AREA_SELECTED(track, TRACK_AREA_PAT)) { - BKE_tracking_marker_clamp(marker, CLAMP_PAT_DIM); + BKE_tracking_marker_clamp_search_size(marker); } if (TRACK_AREA_SELECTED(track, TRACK_AREA_SEARCH)) { - BKE_tracking_marker_clamp(marker, CLAMP_SEARCH_DIM); + BKE_tracking_marker_clamp_search_size(marker); } } else if (t->mode == TFM_ROTATION) { if (TRACK_AREA_SELECTED(track, TRACK_AREA_PAT)) { - BKE_tracking_marker_clamp(marker, CLAMP_PAT_POS); + BKE_tracking_marker_clamp_pattern_position(marker); } } } diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index afad4df2c88..769fd28c57b 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -15,6 +15,7 @@ #include "BLI_math.h" #include "GPU_immediate.h" +#include "GPU_matrix.h" #include "GPU_state.h" #include "BKE_context.h" @@ -25,6 +26,7 @@ #include "RNA_access.h" +#include "WM_api.h" #include "WM_types.h" #include "ED_gizmo_library.h" @@ -173,20 +175,20 @@ void drawSnapping(const struct bContext *C, TransInfo *t) return; } - UI_GetThemeColor3ubv(TH_TRANSFORM, col); - col[3] = 128; - - UI_GetThemeColor3ubv(TH_SELECT, selectedCol); - selectedCol[3] = 128; - - UI_GetThemeColor3ubv(TH_ACTIVE, activeCol); - activeCol[3] = 192; - if (t->spacetype == SPACE_VIEW3D) { bool draw_target = (t->tsnap.status & TARGET_INIT) && (t->tsnap.mode & SCE_SNAP_MODE_EDGE_PERPENDICULAR); if (draw_target || validSnap(t)) { + UI_GetThemeColor3ubv(TH_TRANSFORM, col); + col[3] = 128; + + UI_GetThemeColor3ubv(TH_SELECT, selectedCol); + selectedCol[3] = 128; + + UI_GetThemeColor3ubv(TH_ACTIVE, activeCol); + activeCol[3] = 192; + const float *loc_cur = NULL; const float *loc_prev = NULL; const float *normal = NULL; @@ -240,8 +242,26 @@ void drawSnapping(const struct bContext *C, TransInfo *t) } else if (t->spacetype == SPACE_IMAGE) { if (validSnap(t)) { - /* This will not draw, and I'm nor sure why - campbell */ - /* TODO: see 2.7x for non-working code */ + uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + float x, y; + const float snap_point[2] = { + t->tsnap.snapPoint[0] / t->aspect[0], + t->tsnap.snapPoint[1] / t->aspect[1], + }; + UI_view2d_view_to_region_fl(&t->region->v2d, UNPACK2(snap_point), &x, &y); + float radius = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE) * U.pixelsize; + + GPU_matrix_push_projection(); + wmOrtho2_region_pixelspace(t->region); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3ub(255, 255, 255); + imm_draw_circle_wire_2d(pos, x, y, radius, 8); + immUnbindProgram(); + + GPU_matrix_pop_projection(); } } else if (t->spacetype == SPACE_NODE) { @@ -990,17 +1010,19 @@ static void snap_calc_uv_fn(TransInfo *t, float *UNUSED(vec)) { BLI_assert(t->spacetype == SPACE_IMAGE); if (t->tsnap.mode & SCE_SNAP_MODE_VERTEX) { - float co[2]; - - UI_view2d_region_to_view(&t->region->v2d, t->mval[0], t->mval[1], &co[0], &co[1]); - uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( t->view_layer, NULL, &objects_len); - float dist_sq = FLT_MAX; - if (ED_uvedit_nearest_uv_multi( - t->scene, objects, objects_len, co, &dist_sq, t->tsnap.snapPoint)) { + float dist_sq = square_f((float)SNAP_MIN_DISTANCE); + if (ED_uvedit_nearest_uv_multi(&t->region->v2d, + t->scene, + objects, + objects_len, + t->mval, + t->tsnap.modeSelect == SNAP_NOT_SELECTED, + &dist_sq, + t->tsnap.snapPoint)) { t->tsnap.snapPoint[0] *= t->aspect[0]; t->tsnap.snapPoint[1] *= t->aspect[1]; diff --git a/source/blender/editors/util/ed_util_ops.cc b/source/blender/editors/util/ed_util_ops.cc index 803e65590a0..af3589e50f0 100644 --- a/source/blender/editors/util/ed_util_ops.cc +++ b/source/blender/editors/util/ed_util_ops.cc @@ -67,19 +67,19 @@ static bool lib_id_preview_editing_poll(bContext *C) static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op) { - char path[FILE_MAX]; + char filepath[FILE_MAX]; - RNA_string_get(op->ptr, "filepath", path); + RNA_string_get(op->ptr, "filepath", filepath); - if (!BLI_is_file(path)) { - BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", path); + if (!BLI_is_file(filepath)) { + BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", filepath); return OPERATOR_CANCELLED; } PointerRNA idptr = CTX_data_pointer_get(C, "id"); ID *id = (ID *)idptr.data; - BKE_previewimg_id_custom_set(id, path); + BKE_previewimg_id_custom_set(id, filepath); WM_event_add_notifier(C, NC_ASSET | NA_EDITED, nullptr); diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 6405d2df66a..ead017a91bf 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -1022,8 +1022,13 @@ bool uv_find_nearest_vert_multi(Scene *scene, return found; } -bool ED_uvedit_nearest_uv( - const Scene *scene, Object *obedit, const float co[2], float *dist_sq, float r_uv[2]) +static bool uvedit_nearest_uv(const Scene *scene, + Object *obedit, + const float co[2], + const float scale[2], + const bool ignore_selected, + float *dist_sq, + float r_uv[2]) { BMEditMesh *em = BKE_editmesh_from_object(obedit); BMIter iter; @@ -1038,8 +1043,14 @@ bool ED_uvedit_nearest_uv( BMLoop *l_iter, *l_first; l_iter = l_first = BM_FACE_FIRST_LOOP(efa); do { + if (ignore_selected && uvedit_uv_select_test(scene, l_iter, cd_loop_uv_offset)) { + continue; + } + const float *uv = ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset))->uv; - const float dist_test = len_squared_v2v2(co, uv); + float co_tmp[2]; + mul_v2_v2v2(co_tmp, scale, uv); + const float dist_test = len_squared_v2v2(co, co_tmp); if (dist_best > dist_test) { dist_best = dist_test; uv_best = uv; @@ -1055,17 +1066,27 @@ bool ED_uvedit_nearest_uv( return false; } -bool ED_uvedit_nearest_uv_multi(const Scene *scene, +bool ED_uvedit_nearest_uv_multi(const View2D *v2d, + const Scene *scene, Object **objects, const uint objects_len, - const float co[2], + const int mval[2], + const bool ignore_selected, float *dist_sq, float r_uv[2]) { bool found = false; + + float scale[2], offset[2]; + UI_view2d_scale_get(v2d, &scale[0], &scale[1]); + UI_view2d_view_to_region_fl(v2d, 0.0f, 0.0f, &offset[0], &offset[1]); + + float co[2]; + sub_v2_v2v2(co, (float[2]){UNPACK2(mval)}, offset); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; - if (ED_uvedit_nearest_uv(scene, obedit, co, dist_sq, r_uv)) { + if (uvedit_nearest_uv(scene, obedit, co, scale, ignore_selected, dist_sq, r_uv)) { found = true; } } diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp index 9304e6aa3fb..e76e74b89e4 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp @@ -370,7 +370,7 @@ int BlenderFileLoader::testDegenerateTriangle(float v1[3], float v2[3], float v3 return 0; } -static bool testEdgeMark(Mesh *me, FreestyleEdge *fed, const MLoopTri *lt, int i) +static bool testEdgeMark(Mesh *me, const FreestyleEdge *fed, const MLoopTri *lt, int i) { MLoop *mloop = &me->mloop[lt->tri[i]]; MLoop *mloop_next = &me->mloop[lt->tri[(i + 1) % 3]]; @@ -395,7 +395,7 @@ void BlenderFileLoader::insertShapeNode(Object *ob, Mesh *me, int id) // Compute loop normals BKE_mesh_calc_normals_split(me); - float(*lnors)[3] = nullptr; + const float(*lnors)[3] = nullptr; if (CustomData_has_layer(&me->ldata, CD_NORMAL)) { lnors = (float(*)[3])CustomData_get_layer(&me->ldata, CD_NORMAL); @@ -405,8 +405,8 @@ void BlenderFileLoader::insertShapeNode(Object *ob, Mesh *me, int id) MVert *mvert = me->mvert; MLoop *mloop = me->mloop; MPoly *mpoly = me->mpoly; - FreestyleEdge *fed = (FreestyleEdge *)CustomData_get_layer(&me->edata, CD_FREESTYLE_EDGE); - FreestyleFace *ffa = (FreestyleFace *)CustomData_get_layer(&me->pdata, CD_FREESTYLE_FACE); + const FreestyleEdge *fed = (FreestyleEdge *)CustomData_get_layer(&me->edata, CD_FREESTYLE_EDGE); + const FreestyleFace *ffa = (FreestyleFace *)CustomData_get_layer(&me->pdata, CD_FREESTYLE_FACE); // Compute view matrix Object *ob_camera_eval = DEG_get_evaluated_object(_depsgraph, RE_GetCamera(_re)); diff --git a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp index 03e9134c6c2..979673fd736 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp @@ -499,9 +499,9 @@ void BlenderStrokeRenderer::test_strip_visibility(Strip::vertex_container &strip StrokeVertexRep *svRep[3]; bool visible; - // iterate over all vertices and count visible faces and strip segments - // (note: a strip segment is a series of visible faces, while two strip - // segments are separated by one or more invisible faces) + /* Iterate over all vertices and count visible faces and strip segments + * (NOTE: a strip segment is a series of visible faces, while two strip + * segments are separated by one or more invisible faces). */ v[0] = strip_vertices.begin(); v[1] = v[0] + 1; v[2] = v[0] + 2; diff --git a/source/blender/freestyle/intern/geometry/SweepLine.h b/source/blender/freestyle/intern/geometry/SweepLine.h index 1165e1bf064..c170ee4d122 100644 --- a/source/blender/freestyle/intern/geometry/SweepLine.h +++ b/source/blender/freestyle/intern/geometry/SweepLine.h @@ -183,7 +183,7 @@ template<class T1, class T2> struct binary_rule { binary_rule() { } - template<class T3, class T4> binary_rule(const binary_rule<T3, T4> &brother) + template<class T3, class T4> binary_rule(const binary_rule<T3, T4> & /*brother*/) { } virtual ~binary_rule() diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index 0005d788a3b..46051a58e8b 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -372,7 +372,7 @@ class FieldEvaluator : NonMovable, NonCopyable { /** * \param field: Field to add to the evaluator. * \param dst: Mutable span that the evaluated result for this field is be written into. - * \note: When the output may only be used as a single value, the version of this function with + * \note When the output may only be used as a single value, the version of this function with * a virtual array result array should be used. */ int add_with_destination(GField field, GMutableSpan dst); @@ -380,7 +380,7 @@ class FieldEvaluator : NonMovable, NonCopyable { /** * \param field: Field to add to the evaluator. * \param dst: Mutable span that the evaluated result for this field is be written into. - * \note: When the output may only be used as a single value, the version of this function with + * \note When the output may only be used as a single value, the version of this function with * a virtual array result array should be used. */ template<typename T> int add_with_destination(Field<T> field, MutableSpan<T> dst) diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index 8716d6c8f67..010c327d482 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -16,15 +16,19 @@ set(INC set(SRC intern/mesh_merge_by_distance.cc + intern/mesh_primitive_cuboid.cc intern/mesh_to_curve_convert.cc intern/point_merge_by_distance.cc intern/realize_instances.cc + intern/resample_curves.cc intern/uv_parametrizer.c GEO_mesh_merge_by_distance.hh + GEO_mesh_primitive_cuboid.hh GEO_mesh_to_curve.hh GEO_point_merge_by_distance.hh GEO_realize_instances.hh + GEO_resample_curves.hh GEO_uv_parametrizer.h ) diff --git a/source/blender/geometry/GEO_mesh_primitive_cuboid.hh b/source/blender/geometry/GEO_mesh_primitive_cuboid.hh new file mode 100644 index 00000000000..6107b15b62d --- /dev/null +++ b/source/blender/geometry/GEO_mesh_primitive_cuboid.hh @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_math_vec_types.hh" + +struct Mesh; +namespace blender { +namespace bke { +class AttributeIDRef; +} +} // namespace blender + +namespace blender::geometry { + +Mesh *create_cuboid_mesh( + const float3 &size, int verts_x, int verts_y, int verts_z, const bke::AttributeIDRef &uv_id); + +Mesh *create_cuboid_mesh(const float3 &size, int verts_x, int verts_y, int verts_z); + +} // namespace blender::geometry diff --git a/source/blender/geometry/GEO_resample_curves.hh b/source/blender/geometry/GEO_resample_curves.hh new file mode 100644 index 00000000000..97399ccb0a5 --- /dev/null +++ b/source/blender/geometry/GEO_resample_curves.hh @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "FN_field.hh" + +#include "BKE_geometry_set.hh" + +struct Curves; + +namespace blender::geometry { + +/** + * Create new curves where the selected curves have been resampled with a number of uniform-length + * samples defined by the count field. Interpolate attributes to the result, with an accuracy that + * depends on the curve's resolution parameter. + * + * \note The values provided by the #count_field are clamped to 1 or greater. + */ +Curves *resample_to_count(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<int> &count_field); + +/** + * Create new curves resampled to make each segment have the length specified by the + * #segment_length field input, rounded to make the length of each segment the same. + * The accuracy will depend on the curve's resolution parameter. + */ +Curves *resample_to_length(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<float> &segment_length_field); + +/** + * Evaluate each selected curve to its implicit evaluated points. + */ +Curves *resample_to_evaluated(const CurveComponent &src_component, + const fn::Field<bool> &selection_field); + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_primitive_cuboid.cc b/source/blender/geometry/intern/mesh_primitive_cuboid.cc new file mode 100644 index 00000000000..07ac2419ad9 --- /dev/null +++ b/source/blender/geometry/intern/mesh_primitive_cuboid.cc @@ -0,0 +1,431 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_index_range.hh" +#include "BLI_math_vector.h" +#include "BLI_math_vector.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_access.hh" +#include "BKE_geometry_set.hh" +#include "BKE_mesh.h" + +#include "GEO_mesh_primitive_cuboid.hh" + +namespace blender::geometry { + +struct CuboidConfig { + float3 size; + int verts_x; + int verts_y; + int verts_z; + int edges_x; + int edges_y; + int edges_z; + int vertex_count; + int poly_count; + int loop_count; + + CuboidConfig(float3 size, int verts_x, int verts_y, int verts_z) + : size(size), + verts_x(verts_x), + verts_y(verts_y), + verts_z(verts_z), + edges_x(verts_x - 1), + edges_y(verts_y - 1), + edges_z(verts_z - 1) + { + BLI_assert(edges_x > 0 && edges_y > 0 && edges_z > 0); + this->vertex_count = this->get_vertex_count(); + this->poly_count = this->get_poly_count(); + this->loop_count = this->poly_count * 4; + } + + private: + int get_vertex_count() + { + const int inner_position_count = (verts_x - 2) * (verts_y - 2) * (verts_z - 2); + return verts_x * verts_y * verts_z - inner_position_count; + } + + int get_poly_count() + { + return 2 * (edges_x * edges_y + edges_y * edges_z + edges_z * edges_x); + } +}; + +static void calculate_vertices(const CuboidConfig &config, MutableSpan<MVert> verts) +{ + const float z_bottom = -config.size.z / 2.0f; + const float z_delta = config.size.z / config.edges_z; + + const float x_left = -config.size.x / 2.0f; + const float x_delta = config.size.x / config.edges_x; + + const float y_front = -config.size.y / 2.0f; + const float y_delta = config.size.y / config.edges_y; + + int vert_index = 0; + + for (const int z : IndexRange(config.verts_z)) { + if (ELEM(z, 0, config.edges_z)) { + /* Fill bottom and top. */ + const float z_pos = z_bottom + z_delta * z; + for (const int y : IndexRange(config.verts_y)) { + const float y_pos = y_front + y_delta * y; + for (const int x : IndexRange(config.verts_x)) { + const float x_pos = x_left + x_delta * x; + copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); + } + } + } + else { + for (const int y : IndexRange(config.verts_y)) { + if (ELEM(y, 0, config.edges_y)) { + /* Fill y-sides. */ + const float y_pos = y_front + y_delta * y; + const float z_pos = z_bottom + z_delta * z; + for (const int x : IndexRange(config.verts_x)) { + const float x_pos = x_left + x_delta * x; + copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); + } + } + else { + /* Fill x-sides. */ + const float x_pos = x_left; + const float y_pos = y_front + y_delta * y; + const float z_pos = z_bottom + z_delta * z; + copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); + const float x_pos2 = x_left + x_delta * config.edges_x; + copy_v3_v3(verts[vert_index++].co, float3(x_pos2, y_pos, z_pos)); + } + } + } + } +} + +/* vert_1 = bottom left, vert_2 = bottom right, vert_3 = top right, vert_4 = top left. + * Hence they are passed as 1,4,3,2 when calculating polys clockwise, and 1,2,3,4 for + * anti-clockwise. + */ +static void define_quad(MutableSpan<MPoly> polys, + MutableSpan<MLoop> loops, + const int poly_index, + const int loop_index, + const int vert_1, + const int vert_2, + const int vert_3, + const int vert_4) +{ + MPoly &poly = polys[poly_index]; + poly.loopstart = loop_index; + poly.totloop = 4; + + MLoop &loop_1 = loops[loop_index]; + loop_1.v = vert_1; + MLoop &loop_2 = loops[loop_index + 1]; + loop_2.v = vert_2; + MLoop &loop_3 = loops[loop_index + 2]; + loop_3.v = vert_3; + MLoop &loop_4 = loops[loop_index + 3]; + loop_4.v = vert_4; +} + +static void calculate_polys(const CuboidConfig &config, + MutableSpan<MPoly> polys, + MutableSpan<MLoop> loops) +{ + int loop_index = 0; + int poly_index = 0; + + /* Number of vertices in an XY cross-section of the cube (barring top and bottom faces). */ + const int xy_cross_section_vert_count = config.verts_x * config.verts_y - + (config.verts_x - 2) * (config.verts_y - 2); + + /* Calculate polys for Bottom faces. */ + int vert_1_start = 0; + + for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + const int vert_1 = vert_1_start + x; + const int vert_2 = vert_1_start + config.verts_x + x; + const int vert_3 = vert_2 + 1; + const int vert_4 = vert_1 + 1; + + define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); + loop_index += 4; + poly_index++; + } + vert_1_start += config.verts_x; + } + + /* Calculate polys for Front faces. */ + vert_1_start = 0; + int vert_2_start = config.verts_x * config.verts_y; + + for ([[maybe_unused]] const int z : IndexRange(config.edges_z)) { + for (const int x : IndexRange(config.edges_x)) { + define_quad(polys, + loops, + poly_index, + loop_index, + vert_1_start + x, + vert_1_start + x + 1, + vert_2_start + x + 1, + vert_2_start + x); + loop_index += 4; + poly_index++; + } + vert_1_start = vert_2_start; + vert_2_start += config.verts_x * config.verts_y - (config.verts_x - 2) * (config.verts_y - 2); + } + + /* Calculate polys for Top faces. */ + vert_1_start = config.verts_x * config.verts_y + + (config.verts_z - 2) * (config.verts_x * config.verts_y - + (config.verts_x - 2) * (config.verts_y - 2)); + vert_2_start = vert_1_start + config.verts_x; + + for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + define_quad(polys, + loops, + poly_index, + loop_index, + vert_1_start + x, + vert_1_start + x + 1, + vert_2_start + x + 1, + vert_2_start + x); + loop_index += 4; + poly_index++; + } + vert_2_start += config.verts_x; + vert_1_start += config.verts_x; + } + + /* Calculate polys for Back faces. */ + vert_1_start = config.verts_x * config.edges_y; + vert_2_start = vert_1_start + xy_cross_section_vert_count; + + for (const int z : IndexRange(config.edges_z)) { + if (z == (config.edges_z - 1)) { + vert_2_start += (config.verts_x - 2) * (config.verts_y - 2); + } + for (const int x : IndexRange(config.edges_x)) { + define_quad(polys, + loops, + poly_index, + loop_index, + vert_1_start + x, + vert_2_start + x, + vert_2_start + x + 1, + vert_1_start + x + 1); + loop_index += 4; + poly_index++; + } + vert_2_start += xy_cross_section_vert_count; + vert_1_start += xy_cross_section_vert_count; + } + + /* Calculate polys for Left faces. */ + vert_1_start = 0; + vert_2_start = config.verts_x * config.verts_y; + + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + int vert_1; + int vert_2; + int vert_3; + int vert_4; + + if (z == 0 || y == 0) { + vert_1 = vert_1_start + config.verts_x * y; + vert_4 = vert_1 + config.verts_x; + } + else { + vert_1 = vert_1_start + 2 * y; + vert_1 += config.verts_x - 2; + vert_4 = vert_1 + 2; + } + + if (y == 0 || z == (config.edges_z - 1)) { + vert_2 = vert_2_start + config.verts_x * y; + vert_3 = vert_2 + config.verts_x; + } + else { + vert_2 = vert_2_start + 2 * y; + vert_2 += config.verts_x - 2; + vert_3 = vert_2 + 2; + } + + define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); + loop_index += 4; + poly_index++; + } + if (z == 0) { + vert_1_start += config.verts_x * config.verts_y; + } + else { + vert_1_start += xy_cross_section_vert_count; + } + vert_2_start += xy_cross_section_vert_count; + } + + /* Calculate polys for Right faces. */ + vert_1_start = config.edges_x; + vert_2_start = vert_1_start + config.verts_x * config.verts_y; + + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + int vert_1 = vert_1_start; + int vert_2 = vert_2_start; + int vert_3 = vert_2_start + 2; + int vert_4 = vert_1 + config.verts_x; + + if (z == 0) { + vert_1 = vert_1_start + config.verts_x * y; + vert_4 = vert_1 + config.verts_x; + } + else { + vert_1 = vert_1_start + 2 * y; + vert_4 = vert_1 + 2; + } + + if (z == (config.edges_z - 1)) { + vert_2 = vert_2_start + config.verts_x * y; + vert_3 = vert_2 + config.verts_x; + } + else { + vert_2 = vert_2_start + 2 * y; + vert_3 = vert_2 + 2; + } + + if (y == (config.edges_y - 1)) { + vert_3 = vert_2 + config.verts_x; + vert_4 = vert_1 + config.verts_x; + } + + define_quad(polys, loops, poly_index, loop_index, vert_1, vert_4, vert_3, vert_2); + loop_index += 4; + poly_index++; + } + if (z == 0) { + vert_1_start += config.verts_x * config.verts_y; + } + else { + vert_1_start += xy_cross_section_vert_count; + } + vert_2_start += xy_cross_section_vert_count; + } +} + +static void calculate_uvs(const CuboidConfig &config, Mesh *mesh, const bke::AttributeIDRef &uv_id) +{ + MeshComponent mesh_component; + mesh_component.replace(mesh, GeometryOwnershipType::Editable); + bke::OutputAttribute_Typed<float2> uv_attribute = + mesh_component.attribute_try_get_for_output_only<float2>(uv_id, ATTR_DOMAIN_CORNER); + MutableSpan<float2> uvs = uv_attribute.as_span(); + + int loop_index = 0; + + const float x_delta = 0.25f / static_cast<float>(config.edges_x); + const float y_delta = 0.25f / static_cast<float>(config.edges_y); + const float z_delta = 0.25f / static_cast<float>(config.edges_z); + + /* Calculate bottom face UVs. */ + for (const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - y * y_delta); + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - (y + 1) * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - (y + 1) * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - y * y_delta); + } + } + + /* Calculate front face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + (z + 1) * z_delta); + } + } + + /* Calculate top face UVs. */ + for (const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + y * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + y * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + (y + 1) * y_delta); + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + (y + 1) * y_delta); + } + } + + /* Calculate back face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + z * z_delta); + } + } + + /* Calculate left face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + z * z_delta); + } + } + + /* Calculate right face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + (z + 1) * z_delta); + } + } + + uv_attribute.save(); +} + +Mesh *create_cuboid_mesh(const float3 &size, + const int verts_x, + const int verts_y, + const int verts_z, + const bke::AttributeIDRef &uv_id) +{ + const CuboidConfig config(size, verts_x, verts_y, verts_z); + + Mesh *mesh = BKE_mesh_new_nomain( + config.vertex_count, 0, 0, config.loop_count, config.poly_count); + + calculate_vertices(config, {mesh->mvert, mesh->totvert}); + + calculate_polys(config, {mesh->mpoly, mesh->totpoly}, {mesh->mloop, mesh->totloop}); + BKE_mesh_calc_edges(mesh, false, false); + + if (uv_id) { + calculate_uvs(config, mesh, uv_id); + } + + return mesh; +} + +Mesh *create_cuboid_mesh(const float3 &size, + const int verts_x, + const int verts_y, + const int verts_z) +{ + return create_cuboid_mesh(size, verts_x, verts_y, verts_z, {}); +} + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/point_merge_by_distance.cc b/source/blender/geometry/intern/point_merge_by_distance.cc index 09ca83ef976..6639ff650d3 100644 --- a/source/blender/geometry/intern/point_merge_by_distance.cc +++ b/source/blender/geometry/intern/point_merge_by_distance.cc @@ -97,9 +97,9 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points, const int merge_index = merge_indices[i]; const int dst_index = src_to_dst_indices[merge_index]; - const IndexRange point_range(map_offsets[dst_index], - map_offsets[dst_index + 1] - map_offsets[dst_index]); - MutableSpan<int> point_merge_indices = merge_map.as_mutable_span().slice(point_range); + const IndexRange points(map_offsets[dst_index], + map_offsets[dst_index + 1] - map_offsets[dst_index]); + MutableSpan<int> point_merge_indices = merge_map.as_mutable_span().slice(points); point_merge_indices[point_merge_counts[dst_index]] = i; point_merge_counts[dst_index]++; } @@ -116,9 +116,8 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points, threading::parallel_for(IndexRange(dst_size), 1024, [&](IndexRange range) { for (const int i_dst : range) { - const IndexRange point_range(map_offsets[i_dst], - map_offsets[i_dst + 1] - map_offsets[i_dst]); - dst_ids[i_dst] = src_ids[point_range.first()]; + const IndexRange points(map_offsets[i_dst], map_offsets[i_dst + 1] - map_offsets[i_dst]); + dst_ids[i_dst] = src_ids[points.first()]; } }); @@ -147,9 +146,9 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points, * in the mixer the size of the result point cloud and to improve memory locality. */ attribute_math::DefaultMixer<T> mixer{dst.slice(i_dst, 1)}; - const IndexRange point_range(map_offsets[i_dst], - map_offsets[i_dst + 1] - map_offsets[i_dst]); - Span<int> src_merge_indices = merge_map.as_span().slice(point_range); + const IndexRange points(map_offsets[i_dst], + map_offsets[i_dst + 1] - map_offsets[i_dst]); + Span<int> src_merge_indices = merge_map.as_span().slice(points); for (const int i_src : src_merge_indices) { mixer.mix_in(0, src[i_src]); } diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index f3f0e5b1fce..ae07e817c67 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -374,7 +374,7 @@ static Vector<std::pair<int, GSpan>> prepare_attribute_fallbacks( } /* Convert the attribute on the instances component to the expected attribute type. */ std::unique_ptr<GArray<>> temporary_array = std::make_unique<GArray<>>( - to_type, instances_component.instances_amount()); + to_type, instances_component.instances_num()); conversions.convert_to_initialized_n(span, temporary_array->as_mutable_span()); span = temporary_array->as_span(); gather_info.r_temporary_arrays.append(std::move(temporary_array)); @@ -548,7 +548,7 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, case GEO_COMPONENT_TYPE_CURVE: { const CurveComponent &curve_component = *static_cast<const CurveComponent *>(component); const Curves *curves = curve_component.get_for_read(); - if (curves != nullptr && curves->geometry.curve_size > 0) { + if (curves != nullptr && curves->geometry.curve_num > 0) { const int curve_index = gather_info.curves.order.index_of(curves); const RealizeCurveInfo &curve_info = gather_info.curves.realize_info[curve_index]; gather_info.r_tasks.curve_tasks.append({gather_info.r_offsets.curves_offsets, @@ -556,8 +556,8 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, base_transform, base_instance_context.curves, base_instance_context.id}); - gather_info.r_offsets.curves_offsets.point += curves->geometry.point_size; - gather_info.r_offsets.curves_offsets.curve += curves->geometry.curve_size; + gather_info.r_offsets.curves_offsets.point += curves->geometry.point_num; + gather_info.r_offsets.curves_offsets.curve += curves->geometry.curve_num; } break; } @@ -1052,7 +1052,7 @@ static void gather_curves_to_realize(const GeometrySet &geometry_set, VectorSet<const Curves *> &r_curves) { if (const Curves *curves = geometry_set.get_curves_for_read()) { - if (curves->geometry.curve_size != 0) { + if (curves->geometry.curve_num != 0) { r_curves.add(curves); } } @@ -1215,13 +1215,13 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, const RealizeCurveTask &last_task = tasks.last(); const Curves &last_curves = *last_task.curve_info->curves; - const int points_size = last_task.start_indices.point + last_curves.geometry.point_size; - const int curves_size = last_task.start_indices.curve + last_curves.geometry.curve_size; + const int points_num = last_task.start_indices.point + last_curves.geometry.point_num; + const int curves_num = last_task.start_indices.curve + last_curves.geometry.curve_num; /* Allocate new curves data-block. */ - Curves *dst_curves_id = bke::curves_new_nomain(points_size, curves_size); + Curves *dst_curves_id = bke::curves_new_nomain(points_num, curves_num); bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - dst_curves.offsets_for_write().last() = points_size; + dst_curves.offsets_for_write().last() = points_num; CurveComponent &dst_component = r_realized_geometry.get_component_for_write<CurveComponent>(); dst_component.replace(dst_curves_id); diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc new file mode 100644 index 00000000000..7895225a189 --- /dev/null +++ b/source/blender/geometry/intern/resample_curves.cc @@ -0,0 +1,474 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_length_parameterize.hh" +#include "BLI_task.hh" + +#include "FN_field.hh" +#include "FN_multi_function_builder.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_curves.hh" +#include "BKE_curves_utils.hh" +#include "BKE_geometry_fields.hh" + +#include "GEO_resample_curves.hh" + +namespace blender::geometry { + +static fn::Field<int> get_count_input_max_one(const fn::Field<int> &count_field) +{ + static fn::CustomMF_SI_SO<int, int> max_one_fn( + "Clamp Above One", + [](int value) { return std::max(1, value); }, + fn::CustomMF_presets::AllSpanOrSingle()); + auto clamp_op = std::make_shared<fn::FieldOperation>( + fn::FieldOperation(max_one_fn, {count_field})); + + return fn::Field<int>(std::move(clamp_op)); +} + +static fn::Field<int> get_count_input_from_length(const fn::Field<float> &length_field) +{ + static fn::CustomMF_SI_SI_SO<float, float, int> get_count_fn( + "Length Input to Count", + [](const float curve_length, const float sample_length) { + /* Find the number of sampled segments by dividing the total length by + * the sample length. Then there is one more sampled point than segment. */ + const int count = int(curve_length / sample_length) + 1; + return std::max(1, count); + }, + fn::CustomMF_presets::AllSpanOrSingle()); + + auto get_count_op = std::make_shared<fn::FieldOperation>(fn::FieldOperation( + get_count_fn, + {fn::Field<float>(std::make_shared<bke::CurveLengthFieldInput>()), length_field})); + + return fn::Field<int>(std::move(get_count_op)); +} + +/** + * Return true if the attribute should be copied/interpolated to the result curves. + * Don't output attributes that correspond to curve types that have no curves in the result. + */ +static bool interpolate_attribute_to_curves(const bke::AttributeIDRef &attribute_id, + const std::array<int, CURVE_TYPES_NUM> &type_counts) +{ + if (!attribute_id.is_named()) { + return true; + } + if (ELEM(attribute_id.name(), + "handle_type_left", + "handle_type_right", + "handle_left", + "handle_right")) { + return type_counts[CURVE_TYPE_BEZIER] != 0; + } + if (ELEM(attribute_id.name(), "nurbs_weight")) { + return type_counts[CURVE_TYPE_NURBS] != 0; + } + return true; +} + +/** + * Return true if the attribute should be copied to poly curves. + */ +static bool interpolate_attribute_to_poly_curve(const bke::AttributeIDRef &attribute_id) +{ + static const Set<StringRef> no_interpolation{{ + "handle_type_left", + "handle_type_right", + "handle_position_right", + "handle_position_left", + "nurbs_weight", + }}; + return !(attribute_id.is_named() && no_interpolation.contains(attribute_id.name())); +} + +/** + * Retrieve spans from source and result attributes. + */ +static void retrieve_attribute_spans(const Span<bke::AttributeIDRef> ids, + const CurveComponent &src_component, + CurveComponent &dst_component, + Vector<GSpan> &src, + Vector<GMutableSpan> &dst, + Vector<bke::OutputAttribute> &dst_attributes) +{ + for (const int i : ids.index_range()) { + GVArray src_attribute = src_component.attribute_try_get_for_read(ids[i], ATTR_DOMAIN_POINT); + BLI_assert(src_attribute); + src.append(src_attribute.get_internal_span()); + + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(src_attribute.type()); + bke::OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + ids[i], ATTR_DOMAIN_POINT, data_type); + dst.append(dst_attribute.as_span()); + dst_attributes.append(std::move(dst_attribute)); + } +} + +struct AttributesForInterpolation : NonCopyable, NonMovable { + Vector<GSpan> src; + Vector<GMutableSpan> dst; + + Vector<bke::OutputAttribute> dst_attributes; + + Vector<GSpan> src_no_interpolation; + Vector<GMutableSpan> dst_no_interpolation; +}; + +/** + * Gather a set of all generic attribute IDs to copy to the result curves. + */ +static void gather_point_attributes_to_interpolate(const CurveComponent &src_component, + CurveComponent &dst_component, + AttributesForInterpolation &result) +{ + bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap( + dst_component.get_for_write()->geometry); + + VectorSet<bke::AttributeIDRef> ids; + VectorSet<bke::AttributeIDRef> ids_no_interpolation; + src_component.attribute_foreach( + [&](const bke::AttributeIDRef &id, const AttributeMetaData meta_data) { + if (meta_data.domain != ATTR_DOMAIN_POINT) { + return true; + } + if (!interpolate_attribute_to_curves(id, dst_curves.curve_type_counts())) { + return true; + } + if (interpolate_attribute_to_poly_curve(id)) { + ids.add_new(id); + } + else { + ids_no_interpolation.add_new(id); + } + return true; + }); + + /* Position is handled differently since it has non-generic interpolation for Bezier + * curves and because the evaluated positions are cached for each evaluated point. */ + ids.remove_contained("position"); + + retrieve_attribute_spans( + ids, src_component, dst_component, result.src, result.dst, result.dst_attributes); + + /* Attributes that aren't interpolated like Bezier handles still have to be be copied + * to the result when there are any unselected curves of the corresponding type. */ + retrieve_attribute_spans(ids_no_interpolation, + src_component, + dst_component, + result.src_no_interpolation, + result.dst_no_interpolation, + result.dst_attributes); + + dst_curves.update_customdata_pointers(); +} + +/** + * Copy the provided point attribute values between all curves in the #curve_ranges index + * ranges, assuming that all curves are the same size in #src_curves and #dst_curves. + */ +template<typename T> +static void copy_between_curves(const bke::CurvesGeometry &src_curves, + const bke::CurvesGeometry &dst_curves, + const Span<IndexRange> curve_ranges, + const Span<T> src, + const MutableSpan<T> dst) +{ + threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) { + for (const IndexRange range : curve_ranges.slice(range)) { + const IndexRange src_points = src_curves.points_for_curves(range); + const IndexRange dst_points = dst_curves.points_for_curves(range); + /* The arrays might be large, so a threaded copy might make sense here too. */ + dst.slice(dst_points).copy_from(src.slice(src_points)); + } + }); +} +static void copy_between_curves(const bke::CurvesGeometry &src_curves, + const bke::CurvesGeometry &dst_curves, + const Span<IndexRange> unselected_ranges, + const GSpan src, + const GMutableSpan dst) +{ + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + copy_between_curves(src_curves, dst_curves, unselected_ranges, src.typed<T>(), dst.typed<T>()); + }); +} + +static Curves *resample_to_uniform(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<int> &count_field) +{ + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_component.get_for_read()->geometry); + + /* Create the new curves without any points and evaluate the final count directly + * into the offsets array, in order to be accumulated into offsets later. */ + Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); + bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); + + /* Directly copy curve attributes, since they stay the same (except for curve types). */ + CustomData_copy(&src_curves.curve_data, + &dst_curves.curve_data, + CD_MASK_ALL, + CD_DUPLICATE, + src_curves.curves_num()); + MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); + + bke::GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; + evaluator.set_selection(selection_field); + evaluator.add_with_destination(count_field, dst_offsets); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( + src_curves.curves_range(), nullptr); + + /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */ + bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets); + bke::curves::accumulate_counts_to_offsets(dst_offsets); + dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); + + /* All resampled curves are poly curves. */ + dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); + + VArray<bool> curves_cyclic = src_curves.cyclic(); + VArray<int8_t> curve_types = src_curves.curve_types(); + Span<float3> evaluated_positions = src_curves.evaluated_positions(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + AttributesForInterpolation attributes; + CurveComponent dst_component; + dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); + gather_point_attributes_to_interpolate(src_component, dst_component, attributes); + + src_curves.ensure_evaluated_lengths(); + + /* Sampling arbitrary attributes works by first interpolating them to the curve's standard + * "evaluated points" and then interpolating that result with the uniform samples. This is + * potentially wasteful when down-sampling a curve to many fewer points. There are two possible + * solutions: only sample the necessary points for interpolation, or first sample curve + * parameter/segment indices and evaluate the curve directly. */ + Array<int> sample_indices(dst_curves.points_num()); + Array<float> sample_factors(dst_curves.points_num()); + + /* Use a "for each group of curves: for each attribute: for each curve" pattern to work on + * smaller sections of data that ideally fit into CPU cache better than simply one attribute at a + * time or one curve at a time. */ + threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { + const IndexMask sliced_selection = selection.slice(selection_range); + + Vector<std::byte> evaluated_buffer; + + /* Gather uniform samples based on the accumulated lengths of the original curve. */ + for (const int i_curve : sliced_selection) { + const bool cyclic = curves_cyclic[i_curve]; + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + length_parameterize::create_uniform_samples( + src_curves.evaluated_lengths_for_curve(i_curve, cyclic), + curves_cyclic[i_curve], + sample_indices.as_mutable_span().slice(dst_points), + sample_factors.as_mutable_span().slice(dst_points)); + } + + /* For every attribute, evaluate attributes from every curve in the range in the original + * curve's "evaluated points", then use linear interpolation to sample to the result. */ + for (const int i_attribute : attributes.dst.index_range()) { + attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { + using T = decltype(dummy); + Span<T> src = attributes.src[i_attribute].typed<T>(); + MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); + + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + + if (curve_types[i_curve] == CURVE_TYPE_POLY) { + length_parameterize::linear_interpolation(src.slice(src_points), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst.slice(dst_points)); + } + else { + const int evaluated_size = src_curves.evaluated_points_for_curve(i_curve).size(); + evaluated_buffer.clear(); + evaluated_buffer.resize(sizeof(T) * evaluated_size); + MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>(); + src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated); + + length_parameterize::linear_interpolation(evaluated.as_span(), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst.slice(dst_points)); + } + } + }); + } + + /* Interpolate the evaluated positions to the resampled curves. */ + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + length_parameterize::linear_interpolation(evaluated_positions.slice(src_points), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst_positions.slice(dst_points)); + } + + /* Fill the default value for non-interpolating attributes that still must be copied. */ + for (GMutableSpan dst : attributes.dst_no_interpolation) { + for (const int i_curve : sliced_selection) { + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); + } + } + }); + + /* Any attribute data from unselected curve points can be directly copied. */ + for (const int i : attributes.src.index_range()) { + copy_between_curves( + src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); + } + for (const int i : attributes.src_no_interpolation.index_range()) { + copy_between_curves(src_curves, + dst_curves, + unselected_ranges, + attributes.src_no_interpolation[i], + attributes.dst_no_interpolation[i]); + } + + /* Copy positions for unselected curves. */ + Span<float3> src_positions = src_curves.positions(); + copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); + + for (bke::OutputAttribute &attribute : attributes.dst_attributes) { + attribute.save(); + } + + return dst_curves_id; +} + +Curves *resample_to_count(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<int> &count_field) +{ + return resample_to_uniform(src_component, selection_field, get_count_input_max_one(count_field)); +} + +Curves *resample_to_length(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<float> &segment_length_field) +{ + return resample_to_uniform( + src_component, selection_field, get_count_input_from_length(segment_length_field)); +} + +Curves *resample_to_evaluated(const CurveComponent &src_component, + const fn::Field<bool> &selection_field) +{ + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_component.get_for_read()->geometry); + + bke::GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( + src_curves.curves_range(), nullptr); + + Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); + bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); + + /* Directly copy curve attributes, since they stay the same (except for curve types). */ + CustomData_copy(&src_curves.curve_data, + &dst_curves.curve_data, + CD_MASK_ALL, + CD_DUPLICATE, + src_curves.curves_num()); + /* All resampled curves are poly curves. */ + dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); + MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); + + src_curves.ensure_evaluated_offsets(); + threading::parallel_for(selection.index_range(), 4096, [&](IndexRange range) { + for (const int i : selection.slice(range)) { + dst_offsets[i] = src_curves.evaluated_points_for_curve(i).size(); + } + }); + bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets); + bke::curves::accumulate_counts_to_offsets(dst_offsets); + + dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); + + /* Create the correct number of uniform-length samples for every selected curve. */ + Span<float3> evaluated_positions = src_curves.evaluated_positions(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + AttributesForInterpolation attributes; + CurveComponent dst_component; + dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); + gather_point_attributes_to_interpolate(src_component, dst_component, attributes); + + threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { + const IndexMask sliced_selection = selection.slice(selection_range); + + /* Evaluate generic point attributes directly to the result attributes. */ + for (const int i_attribute : attributes.dst.index_range()) { + attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { + using T = decltype(dummy); + Span<T> src = attributes.src[i_attribute].typed<T>(); + MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); + + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + src_curves.interpolate_to_evaluated( + i_curve, src.slice(src_points), dst.slice(dst_points)); + } + }); + } + + /* Copy the evaluated positions to the selected curves. */ + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + dst_positions.slice(dst_points).copy_from(evaluated_positions.slice(src_points)); + } + + /* Fill the default value for non-interpolating attributes that still must be copied. */ + for (GMutableSpan dst : attributes.dst_no_interpolation) { + for (const int i_curve : sliced_selection) { + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); + } + } + }); + + /* Any attribute data from unselected curve points can be directly copied. */ + for (const int i : attributes.src.index_range()) { + copy_between_curves( + src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); + } + for (const int i : attributes.src_no_interpolation.index_range()) { + copy_between_curves(src_curves, + dst_curves, + unselected_ranges, + attributes.src_no_interpolation[i], + attributes.dst_no_interpolation[i]); + } + + /* Copy positions for unselected curves. */ + Span<float3> src_positions = src_curves.positions(); + copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); + + for (bke::OutputAttribute &attribute : attributes.dst_attributes) { + attribute.save(); + } + + return dst_curves_id; +} + +} // namespace blender::geometry diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt index 6108629183c..69fc26c99e9 100644 --- a/source/blender/gpencil_modifiers/CMakeLists.txt +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -65,14 +65,30 @@ set(SRC # Lineart code intern/lineart/lineart_chain.c + intern/lineart/lineart_cpp_bridge.cc intern/lineart/lineart_cpu.c intern/lineart/lineart_ops.c intern/lineart/lineart_util.c intern/lineart/MOD_lineart.h intern/lineart/lineart_intern.h +) + +if(WITH_TBB) +add_definitions(-DWITH_TBB) +if(WIN32) + # TBB includes Windows.h which will define min/max macros + # that will collide with the stl versions. + add_definitions(-DNOMINMAX) +endif() +list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} +) +list(APPEND LIB + ${TBB_LIBRARIES} ) +endif() set(LIB ) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 1058f861be3..a07ef2eb195 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -218,9 +218,18 @@ static void add_this_collection(Collection *c, if (!c) { return; } + bool default_add = true; + /* Do not do nested collection usage check, this is consistent with lineart calculation, because + * collection usage doesn't have a INHERIT mode. This might initially be derived from the fact + * that an object can be inside multiple collections, but might be irrelevant now with the way + * objects are iterated. Keep this logic for now. */ + if (c->lineart_usage & COLLECTION_LRT_EXCLUDE) { + default_add = false; + } FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (c, ob, mode) { if (ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { - if (ob->lineart.usage != OBJECT_LRT_EXCLUDE) { + if ((ob->lineart.usage == OBJECT_LRT_INHERIT && default_add) || + ob->lineart.usage != OBJECT_LRT_EXCLUDE) { DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_GEOMETRY, "Line Art Modifier"); DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); } @@ -239,15 +248,11 @@ static void updateDepsgraph(GpencilModifierData *md, DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md; - if (lmd->source_type == LRT_SOURCE_OBJECT && lmd->source_object) { - DEG_add_object_relation( - ctx->node, lmd->source_object, DEG_OB_COMP_GEOMETRY, "Line Art Modifier"); - DEG_add_object_relation( - ctx->node, lmd->source_object, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); - } - else { - add_this_collection(ctx->scene->master_collection, ctx, mode); - } + + /* Always add whole master collection because line art will need the whole scene for + * visibility computation. Line art exclusion is handled inside #add_this_collection. */ + add_this_collection(ctx->scene->master_collection, ctx, mode); + if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA && lmd->source_camera) { DEG_add_object_relation( ctx->node, lmd->source_camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); @@ -387,7 +392,6 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayout *col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_remove_doubles", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_edge_overlap", 0, IFACE_("Overlapping Edges As Contour"), ICON_NONE); uiItemR(col, ptr, "use_object_instances", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c index fcf1e28c6da..259e62a249c 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c @@ -122,6 +122,8 @@ static void deformStroke(GpencilModifierData *md, const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname); const bool invert_group = (mmd->flag & GP_NOISE_INVERT_VGROUP) != 0; const bool use_curve = (mmd->flag & GP_NOISE_CUSTOM_CURVE) != 0 && mmd->curve_intensity; + const int cfra = (int)DEG_get_ctime(depsgraph); + const bool is_keyframe = (mmd->noise_mode == GP_NOISE_RANDOM_KEYFRAME); if (!is_stroke_affected_by_modifier(ob, mmd->layername, @@ -148,7 +150,13 @@ static void deformStroke(GpencilModifierData *md, seed += BLI_hash_string(md->name); if (mmd->flag & GP_NOISE_USE_RANDOM) { - seed += ((int)DEG_get_ctime(depsgraph)) / mmd->step; + if (!is_keyframe) { + seed += cfra / mmd->step; + } + else { + /* If change every keyframe, use the last keyframe. */ + seed += gpf->framenum; + } } /* Sanitize as it can create out of bound reads. */ @@ -302,7 +310,12 @@ static void random_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetActive(layout, RNA_boolean_get(ptr, "use_random")); - uiItemR(layout, ptr, "step", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "random_mode", 0, NULL, ICON_NONE); + + const int mode = RNA_enum_get(ptr, "random_mode"); + if (mode != GP_NOISE_RANDOM_KEYFRAME) { + uiItemR(layout, ptr, "step", 0, NULL, ICON_NONE); + } } static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 5d952991cf7..06738ef6a12 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -122,17 +122,13 @@ typedef struct LineartEdge { /** We only need link node kind of list here. */ struct LineartEdge *next; struct LineartVert *v1, *v2; - /** - * Local vertex index for two ends, not pouting in #RenderVert because all verts are loaded, so - * as long as fewer than half of the mesh edges are becoming a feature line, we save more memory. - */ - int v1_obindex, v2_obindex; + struct LineartTriangle *t1, *t2; ListBase segments; char min_occ; /** Also for line type determination on chaining. */ - unsigned char flags; + uint16_t flags; unsigned char intersection_mask; /** @@ -158,6 +154,8 @@ typedef struct LineartEdgeChain { /** Chain now only contains one type of segments */ int type; + /** Will only connect chains that has the same loop id. */ + int loop_id; unsigned char material_mask_bits; unsigned char intersection_mask; @@ -171,7 +169,7 @@ typedef struct LineartEdgeChainItem { /** For restoring position to 3d space. */ float gpos[3]; float normal[3]; - unsigned char line_type; + uint16_t line_type; char occlusion; unsigned char material_mask_bits; unsigned char intersection_mask; @@ -189,6 +187,12 @@ typedef struct LineartChainRegisterEntry { char is_left; } LineartChainRegisterEntry; +typedef struct LineartAdjacentEdge { + unsigned int v1; + unsigned int v2; + unsigned int e; +} LineartAdjacentEdge; + enum eLineArtTileRecursiveLimit { /* If tile gets this small, it's already much smaller than a pixel. No need to continue * splitting. */ @@ -200,6 +204,12 @@ enum eLineArtTileRecursiveLimit { #define LRT_TILE_SPLITTING_TRIANGLE_LIMIT 100 #define LRT_TILE_EDGE_COUNT_INITIAL 32 +typedef struct LineartPendingEdges { + LineartEdge **array; + int max; + int next; +} LineartPendingEdges; + typedef struct LineartRenderBuffer { struct LineartRenderBuffer *prev, *next; @@ -244,15 +254,9 @@ typedef struct LineartRenderBuffer { int triangle_size; - /* Although using ListBase here, LineartEdge is single linked list. - * list.last is used to store worker progress along the list. - * See lineart_main_occlusion_begin() for more info. */ - ListBase contour; - ListBase intersection; - ListBase crease; - ListBase material; - ListBase edge_mark; - ListBase floating; + /* Note: Data inside #pending_edges are allocated with MEM_xxx call instead of in pool. */ + struct LineartPendingEdges pending_edges; + int scheduled_count; ListBase chains; @@ -358,14 +362,9 @@ typedef struct LineartRenderTaskInfo { int thread_id; - /* These lists only denote the part of the main edge list that the thread should iterate over. - * Be careful to not iterate outside of these bounds as it is not thread safe to do so. */ - ListBase contour; - ListBase intersection; - ListBase crease; - ListBase material; - ListBase edge_mark; - ListBase floating; + /* #pending_edges here only stores a refernce to a portion in LineartRenderbuffer::pending_edges, + * assigned by the occlusion scheduler. */ + struct LineartPendingEdges pending_edges; } LineartRenderTaskInfo; @@ -383,20 +382,14 @@ typedef struct LineartObjectInfo { bool free_use_mesh; - /* Threads will add lines inside here, when all threads are done, we combine those into the - * ones in LineartRenderBuffer. */ - ListBase contour; - ListBase intersection; - ListBase crease; - ListBase material; - ListBase edge_mark; - ListBase floating; + /* Note: Data inside #pending_edges are allocated with MEM_xxx call instead of in pool. */ + struct LineartPendingEdges pending_edges; } LineartObjectInfo; typedef struct LineartObjectLoadTaskInfo { struct LineartRenderBuffer *rb; - struct Depsgraph *dg; + int thread_id; /* LinkNode styled list */ LineartObjectInfo *pending; /* Used to spread the load across several threads. This can not overflow. */ @@ -481,7 +474,7 @@ typedef struct LineartBoundingArea { * r_aligned: True when 1) a and b is exactly on the same straight line and 2) a and b share a * common end-point. * - * Important: if r_aligned is true, r_ratio will be either 0 or 1 depending on which point from + * IMPORTANT: if r_aligned is true, r_ratio will be either 0 or 1 depending on which point from * segment a is shared with segment b. If it's a1 then r_ratio is 0, else then r_ratio is 1. This * extra information is needed for line art occlusion stage to work correctly in such cases. */ diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index b666eb677eb..226b8f532b5 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -219,7 +219,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) e->flags, es->occlusion, es->material_mask_bits, - e->v1_obindex); + e->v1->index); while (ba && (new_e = lineart_line_get_connected( ba, new_vt, &new_vt, e->flags, e->intersection_mask))) { new_e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; @@ -256,7 +256,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) new_e->flags, es->occlusion, es->material_mask_bits, - new_e->v1_obindex); + new_e->v1->index); last_occlusion = es->occlusion; last_transparency = es->material_mask_bits; } @@ -282,7 +282,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) new_e->flags, last_occlusion, last_transparency, - new_e->v2_obindex); + new_e->v2->index); last_occlusion = es->occlusion; last_transparency = es->material_mask_bits; } @@ -295,7 +295,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) new_e->flags, last_occlusion, last_transparency, - new_e->v2_obindex); + new_e->v2->index); } ba = MOD_lineart_get_bounding_area(rb, new_vt->fbcoord[0], new_vt->fbcoord[1]); } @@ -336,7 +336,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) e->flags, es->occlusion, es->material_mask_bits, - e->v1_obindex); + e->v1->index); last_occlusion = es->occlusion; last_transparency = es->material_mask_bits; } @@ -349,7 +349,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) e->flags, last_occlusion, last_transparency, - e->v2_obindex); + e->v2->index); /* Step 3: grow right. */ ba = MOD_lineart_get_bounding_area(rb, e->v2->fbcoord[0], e->v2->fbcoord[1]); @@ -402,7 +402,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) new_e->flags, last_occlusion, last_transparency, - new_e->v1_obindex); + new_e->v1->index); } } else if (new_vt == new_e->v2) { @@ -428,7 +428,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) new_e->flags, es->occlusion, es->material_mask_bits, - new_e->v2_obindex); + new_e->v2->index); last_occlusion = es->occlusion; last_transparency = es->material_mask_bits; } @@ -441,7 +441,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) new_e->flags, last_occlusion, last_transparency, - new_e->v2_obindex); + new_e->v2->index); } ba = MOD_lineart_get_bounding_area(rb, new_vt->fbcoord[0], new_vt->fbcoord[1]); } @@ -617,9 +617,14 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) rb->chains.last = rb->chains.first = NULL; + int loop_id = 0; while ((ec = BLI_pophead(&swap)) != NULL) { ec->next = ec->prev = NULL; BLI_addtail(&rb->chains, ec); + + ec->loop_id = loop_id; + loop_id++; + LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first; int fixed_occ = first_eci->occlusion; unsigned char fixed_mask = first_eci->material_mask_bits; @@ -651,6 +656,7 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) new_ec = lineart_chain_create(rb); new_ec->chain.first = eci; new_ec->chain.last = ec->chain.last; + new_ec->loop_id = loop_id; ec->chain.last = eci->prev; ((LineartEdgeChainItem *)ec->chain.last)->next = 0; eci->prev = 0; @@ -743,6 +749,7 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf int occlusion, unsigned char material_mask_bits, unsigned char isec_mask, + int loop_id, float dist, float *result_new_len, LineartBoundingArea *caller_ba) @@ -791,7 +798,11 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf float new_len = rb->use_geometry_space_chain ? len_v3v3(cre->eci->gpos, eci->gpos) : len_v2v2(cre->eci->pos, eci->pos); - if (new_len < dist) { + /* Even if the vertex is not from the same contour loop, we try to chain it still if the + * distance is small enough. This way we can better chain smaller loops and smooth them out + * later. */ + if (((cre->ec->loop_id == loop_id) && (new_len < dist)) || + ((cre->ec->loop_id != loop_id) && (new_len < dist / 10))) { closest_cre = cre; dist = new_len; if (result_new_len) { @@ -815,6 +826,7 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf occlusion, \ material_mask_bits, \ isec_mask, \ + loop_id, \ dist, \ &adjacent_new_len, \ ba); \ @@ -844,7 +856,7 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb) LineartChainRegisterEntry *closest_cre_l, *closest_cre_r, *closest_cre; float dist = rb->chaining_image_threshold; float dist_l, dist_r; - int occlusion, reverse_main; + int occlusion, reverse_main, loop_id; unsigned char material_mask_bits, isec_mask; ListBase swap = {0}; @@ -863,6 +875,7 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb) continue; } BLI_addtail(&rb->chains, ec); + loop_id = ec->loop_id; if (ec->type == LRT_EDGE_FLAG_LOOSE && (!rb->use_loose_edge_chain)) { continue; @@ -876,10 +889,28 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb) eci_r = ec->chain.last; while ((ba_l = lineart_bounding_area_get_end_point(rb, eci_l)) && (ba_r = lineart_bounding_area_get_end_point(rb, eci_r))) { - closest_cre_l = lineart_chain_get_closest_cre( - rb, ba_l, ec, eci_l, occlusion, material_mask_bits, isec_mask, dist, &dist_l, NULL); - closest_cre_r = lineart_chain_get_closest_cre( - rb, ba_r, ec, eci_r, occlusion, material_mask_bits, isec_mask, dist, &dist_r, NULL); + closest_cre_l = lineart_chain_get_closest_cre(rb, + ba_l, + ec, + eci_l, + occlusion, + material_mask_bits, + isec_mask, + loop_id, + dist, + &dist_l, + NULL); + closest_cre_r = lineart_chain_get_closest_cre(rb, + ba_r, + ec, + eci_r, + occlusion, + material_mask_bits, + isec_mask, + loop_id, + dist, + &dist_r, + NULL); if (closest_cre_l && closest_cre_r) { if (dist_l < dist_r) { closest_cre = closest_cre_l; @@ -971,27 +1002,67 @@ void MOD_lineart_chain_clear_picked_flag(LineartCache *lc) void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance) { LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { - LineartEdgeChainItem *next_eci; - for (LineartEdgeChainItem *eci = ec->chain.first; eci; eci = next_eci) { - next_eci = eci->next; - LineartEdgeChainItem *eci2, *eci3, *eci4; - - /* Not enough point to do simplify. */ - if ((!(eci2 = eci->next)) || (!(eci3 = eci2->next))) { - continue; - } - - /* No need to care for different line types/occlusion and so on, because at this stage they - * are all the same within a chain. */ - - /* If p3 is within the p1-p2 segment of a width of "tolerance". */ - if (dist_to_line_segment_v2(eci3->pos, eci->pos, eci2->pos) < tolerance) { - /* And if p4 is on the extension of p1-p2 , we remove p3. */ - if ((eci4 = eci3->next) && (dist_to_line_v2(eci4->pos, eci->pos, eci2->pos) < tolerance)) { - BLI_remlink(&ec->chain, eci3); - next_eci = eci; + /* Go through the chain two times, once from each direction. */ + for (int times = 0; times < 2; times++) { + for (LineartEdgeChainItem *eci = ec->chain.first, *next_eci = eci->next; eci; + eci = next_eci) { + LineartEdgeChainItem *eci2, *eci3, *eci4; + + if ((!(eci2 = eci->next)) || (!(eci3 = eci2->next))) { + /* Not enough points to simplify. */ + next_eci = eci->next; + continue; } + /* No need to care for different line types/occlusion and so on, because at this stage they + * are all the same within a chain. + * + * We need to simplify a chain from this: + * 1-----------2 + * 3-----------4 + * to this: + * 1-----------2--_ + * `--4 */ + + /* If p3 is within the p1-p2 segment of a width of "tolerance", in other words, p3 is + * approximately on the segment of p1-p2. */ + if (dist_to_line_segment_v2(eci3->pos, eci->pos, eci2->pos) < tolerance) { + float vec2[2], vec3[2], v2n[2], ratio, len2; + sub_v2_v2v2(vec2, eci2->pos, eci->pos); + sub_v2_v2v2(vec3, eci3->pos, eci->pos); + normalize_v2_v2(v2n, vec2); + ratio = dot_v2v2(v2n, vec3); + len2 = len_v2(vec2); + /* Because this smoothing applies on geometries of different scales in the same scene, + * some small scale features (e.g. the "tails" on the inner ring of a torus geometry) + * could be completely erased if the tolerance value is set for accommodating the entire + * scene. Those situations typically result in (ratio << 0), looks like this: + * 1---2 + * 3-------------------------------4 + * (this sort of long zigzag obviously are "features" that can't be erased) + * setting a ratio of -10 turned out to be a reasonable threshold in tests. */ + if (ratio < len2 && ratio > -len2 * 10) { + /* We only remove p3 if p4 is on the extension of p1->p2. */ + if ((eci4 = eci3->next) && + (dist_to_line_v2(eci4->pos, eci->pos, eci2->pos) < tolerance)) { + BLI_remlink(&ec->chain, eci3); + next_eci = eci; + continue; + } + if (!eci4) { + /* See if the last segment's direction is reversed, if so remove that. + * Basically we don't need to preserve p3 if the entire chain looked like this: + * ...----1----3===2 */ + if (len_v2(vec2) > len_v2(vec3)) { + BLI_remlink(&ec->chain, eci3); + } + next_eci = NULL; + continue; + } + } + } + next_eci = eci->next; } + BLI_listbase_reverse(&ec->chain); } } } @@ -1152,6 +1223,8 @@ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshol new_ec->object_ref = ec->object_ref; new_ec->type = ec->type; new_ec->level = ec->level; + new_ec->loop_id = ec->loop_id; + new_ec->intersection_mask = ec->intersection_mask; new_ec->material_mask_bits = ec->material_mask_bits; ec = new_ec; } diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc new file mode 100644 index 00000000000..174399618a5 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup modifiers + */ + +#include "BLI_sort.hh" +#include "BLI_vector.hh" +#include "MOD_lineart.h" +#include "lineart_intern.h" + +static bool cmp_adjacent_items(const LineartAdjacentEdge &p1, const LineartAdjacentEdge &p2) +{ + int a = p1.v1 - p2.v1; + int b = p1.v2 - p2.v2; + /* parallel_sort() requires cmp() to return true when the first element needs to appear before + * the second element in the sorted array, false otherwise (strict weak ordering), see + * https://en.cppreference.com/w/cpp/named_req/Compare. */ + return a < 0 ? true : (a == 0 ? b < 0 : false); +} + +void lineart_sort_adjacent_items(LineartAdjacentEdge *ai, int length) +{ + blender::parallel_sort(ai, ai + length - 1, cmp_adjacent_items); +} diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 24e11f6be3b..fe1bbc3fc23 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -8,6 +8,7 @@ #include "MOD_gpencil_lineart.h" #include "MOD_lineart.h" +#include "BLI_edgehash.h" #include "BLI_linklist.h" #include "BLI_listbase.h" #include "BLI_math.h" @@ -20,6 +21,7 @@ #include "BKE_collection.h" #include "BKE_customdata.h" #include "BKE_deform.h" +#include "BKE_duplilist.h" #include "BKE_editmesh.h" #include "BKE_global.h" #include "BKE_gpencil.h" @@ -28,6 +30,8 @@ #include "BKE_lib_id.h" #include "BKE_material.h" #include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_mesh_runtime.h" #include "BKE_object.h" #include "BKE_pointcache.h" #include "BKE_scene.h" @@ -93,7 +97,7 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *spl, double *from, double *to); -static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e); +static void lineart_add_edge_to_array(LineartPendingEdges *pe, LineartEdge *e); static LineartCache *lineart_init_cache(void); @@ -408,38 +412,26 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge * static int lineart_occlusion_make_task_info(LineartRenderBuffer *rb, LineartRenderTaskInfo *rti) { - LineartEdge *data; - int i; int res = 0; + int starting_index; BLI_spin_lock(&rb->lock_task); -#define LRT_ASSIGN_OCCLUSION_TASK(name) \ - if (rb->name.last) { \ - data = rb->name.last; \ - rti->name.first = (void *)data; \ - for (i = 0; i < LRT_THREAD_EDGE_COUNT && data; i++) { \ - data = data->next; \ - } \ - rti->name.last = data; \ - rb->name.last = data; \ - res = 1; \ - } \ - else { \ - rti->name.first = rti->name.last = NULL; \ - } - - LRT_ASSIGN_OCCLUSION_TASK(contour); - LRT_ASSIGN_OCCLUSION_TASK(intersection); - LRT_ASSIGN_OCCLUSION_TASK(crease); - LRT_ASSIGN_OCCLUSION_TASK(material); - LRT_ASSIGN_OCCLUSION_TASK(edge_mark); - LRT_ASSIGN_OCCLUSION_TASK(floating); - -#undef LRT_ASSIGN_OCCLUSION_TASK + starting_index = rb->scheduled_count; + rb->scheduled_count += LRT_THREAD_EDGE_COUNT; BLI_spin_unlock(&rb->lock_task); + if (starting_index >= rb->pending_edges.next) { + res = 0; + } + else { + rti->pending_edges.array = &rb->pending_edges.array[starting_index]; + int remaining = rb->pending_edges.next - starting_index; + rti->pending_edges.max = MIN2(remaining, LRT_THREAD_EDGE_COUNT); + res = 1; + } + return res; } @@ -449,28 +441,8 @@ static void lineart_occlusion_worker(TaskPool *__restrict UNUSED(pool), LineartR LineartEdge *eip; while (lineart_occlusion_make_task_info(rb, rti)) { - - for (eip = rti->contour.first; eip && eip != rti->contour.last; eip = eip->next) { - lineart_occlusion_single_line(rb, eip, rti->thread_id); - } - - for (eip = rti->crease.first; eip && eip != rti->crease.last; eip = eip->next) { - lineart_occlusion_single_line(rb, eip, rti->thread_id); - } - - for (eip = rti->intersection.first; eip && eip != rti->intersection.last; eip = eip->next) { - lineart_occlusion_single_line(rb, eip, rti->thread_id); - } - - for (eip = rti->material.first; eip && eip != rti->material.last; eip = eip->next) { - lineart_occlusion_single_line(rb, eip, rti->thread_id); - } - - for (eip = rti->edge_mark.first; eip && eip != rti->edge_mark.last; eip = eip->next) { - lineart_occlusion_single_line(rb, eip, rti->thread_id); - } - - for (eip = rti->floating.first; eip && eip != rti->floating.last; eip = eip->next) { + for (int i = 0; i < rti->pending_edges.max; i++) { + eip = rti->pending_edges.array[i]; lineart_occlusion_single_line(rb, eip, rti->thread_id); } } @@ -488,15 +460,6 @@ static void lineart_main_occlusion_begin(LineartRenderBuffer *rb) "Task Pool"); int i; - /* The "last" entry is used to store worker progress in the whole list. - * These list themselves are single-direction linked, with list.first being the head. */ - rb->contour.last = rb->contour.first; - rb->crease.last = rb->crease.first; - rb->intersection.last = rb->intersection.first; - rb->material.last = rb->material.first; - rb->edge_mark.last = rb->edge_mark.first; - rb->floating.last = rb->floating.first; - TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH); for (i = 0; i < thread_count; i++) { @@ -753,13 +716,12 @@ static bool lineart_edge_match(LineartTriangle *tri, LineartEdge *e, int v1, int (tri->v[v2] == e->v1 && tri->v[v1] == e->v2)); } -static void lineart_discard_duplicated_edges(LineartEdge *old_e, int v1id, int v2id) +static void lineart_discard_duplicated_edges(LineartEdge *old_e) { LineartEdge *e = old_e; - e++; - while (e->v1_obindex == v1id && e->v2_obindex == v2id) { - e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; + while (e->flags & LRT_EDGE_FLAG_NEXT_IS_DUPLICATION) { e++; + e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; } } @@ -789,8 +751,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, int v_count = *r_v_count; int e_count = *r_e_count; int t_count = *r_t_count; - int v1_obi, v2_obi; - char new_flag = 0; + uint16_t new_flag = 0; LineartEdge *new_e, *e, *old_e; LineartEdgeSegment *es; @@ -813,13 +774,9 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, e = new_e; #define INCREASE_EDGE \ - v1_obi = e->v1_obindex; \ - v2_obi = e->v2_obindex; \ new_e = &((LineartEdge *)e_eln->pointer)[e_count]; \ e_count++; \ e = new_e; \ - e->v1_obindex = v1_obi; \ - e->v2_obindex = v2_obi; \ es = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeSegment)); \ BLI_addtail(&e->segments, es); @@ -828,15 +785,17 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, old_e = ta->e[e_num]; \ new_flag = old_e->flags; \ old_e->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ - lineart_discard_duplicated_edges(old_e, old_e->v1_obindex, old_e->v2_obindex); \ + lineart_discard_duplicated_edges(old_e); \ INCREASE_EDGE \ e->v1 = (v1_link); \ e->v2 = (v2_link); \ + e->v1->index = (v1_link)->index; \ + e->v2->index = (v1_link)->index; \ e->flags = new_flag; \ e->object_ref = ob; \ e->t1 = ((old_e->t1 == tri) ? (new_tri) : (old_e->t1)); \ e->t2 = ((old_e->t2 == tri) ? (new_tri) : (old_e->t2)); \ - lineart_add_edge_to_list(rb, e); \ + lineart_add_edge_to_array(&rb->pending_edges, e); \ } #define RELINK_EDGE(e_num, new_tri) \ @@ -849,15 +808,15 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, #define REMOVE_TRIANGLE_EDGE \ if (ta->e[0]) { \ ta->e[0]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ - lineart_discard_duplicated_edges(ta->e[0], ta->e[0]->v1_obindex, ta->e[0]->v2_obindex); \ + lineart_discard_duplicated_edges(ta->e[0]); \ } \ if (ta->e[1]) { \ ta->e[1]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ - lineart_discard_duplicated_edges(ta->e[1], ta->e[1]->v1_obindex, ta->e[1]->v2_obindex); \ + lineart_discard_duplicated_edges(ta->e[1]); \ } \ if (ta->e[2]) { \ ta->e[2]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ - lineart_discard_duplicated_edges(ta->e[2], ta->e[2]->v1_obindex, ta->e[2]->v2_obindex); \ + lineart_discard_duplicated_edges(ta->e[2]); \ } switch (in0 + in1 + in2) { @@ -922,7 +881,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contour.first, e); + lineart_add_edge_to_array(&rb->pending_edges, e); } /* NOTE: inverting `e->v1/v2` (left/right point) doesn't matter as long as * `tri->edge` and `tri->v` has the same sequence. and the winding direction @@ -971,7 +930,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contour.first, e); + lineart_add_edge_to_array(&rb->pending_edges, e); } e->v1 = &vt[0]; e->v2 = &vt[1]; @@ -1012,7 +971,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contour.first, e); + lineart_add_edge_to_array(&rb->pending_edges, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; @@ -1087,7 +1046,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contour.first, e); + lineart_add_edge_to_array(&rb->pending_edges, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; @@ -1139,10 +1098,11 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contour.first, e); + lineart_add_edge_to_array(&rb->pending_edges, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; + e->t1 = tri1; e->object_ref = ob; @@ -1187,10 +1147,11 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contour.first, e); + lineart_add_edge_to_array(&rb->pending_edges, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; + e->t1 = tri1; e->object_ref = ob; @@ -1426,17 +1387,45 @@ static void lineart_main_discard_out_of_frame_edges(LineartRenderBuffer *rb) } } -/** - * Transform a single vert to it's viewing position. - */ -static void lineart_vert_transform( - BMVert *v, int index, LineartVert *RvBuf, double (*mv_mat)[4], double (*mvp_mat)[4]) +typedef struct LineartEdgeNeighbor { + int e; + uint16_t flags; + int v1, v2; +} LineartEdgeNeighbor; + +typedef struct VertData { + MVert *mvert; + LineartVert *v_arr; + double (*model_view)[4]; + double (*model_view_proj)[4]; +} VertData; + +static void lineart_mvert_transform_task(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict UNUSED(tls)) { + VertData *vert_task_data = (VertData *)userdata; + MVert *m_v = &vert_task_data->mvert[i]; double co[4]; - LineartVert *vt = &RvBuf[index]; - copy_v3db_v3fl(co, v->co); - mul_v3_m4v3_db(vt->gloc, mv_mat, co); - mul_v4_m4v3_db(vt->fbcoord, mvp_mat, co); + LineartVert *v = &vert_task_data->v_arr[i]; + copy_v3db_v3fl(co, m_v->co); + mul_v3_m4v3_db(v->gloc, vert_task_data->model_view, co); + mul_v4_m4v3_db(v->fbcoord, vert_task_data->model_view_proj, co); + v->index = i; +} + +#define LRT_EDGE_FLAG_TYPE_MAX_BITS 6 + +static int lineart_edge_type_duplication_count(char eflag) +{ + int count = 0; + /* See eLineartEdgeFlag for details. */ + for (int i = 0; i < LRT_EDGE_FLAG_TYPE_MAX_BITS; i++) { + if (eflag & (1 << i)) { + count++; + } + } + return count; } /** @@ -1452,88 +1441,123 @@ static LineartTriangle *lineart_triangle_from_index(LineartRenderBuffer *rb, return (LineartTriangle *)b; } -static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, - BMEdge *e, - LineartTriangle *rt_array, - LineartVert *rv_array, - float crease_threshold, - bool use_auto_smooth, - bool use_freestyle_edge, - bool use_freestyle_face, - BMesh *bm_if_freestyle) +typedef struct EdgeFeatData { + LineartRenderBuffer *rb; + Mesh *me; + const MLoopTri *mlooptri; + LineartTriangle *tri_array; + LineartVert *v_array; + float crease_threshold; + bool use_auto_smooth; + bool use_freestyle_face; + int freestyle_face_index; + bool use_freestyle_edge; + int freestyle_edge_index; + LineartEdgeNeighbor *edge_nabr; +} EdgeFeatData; + +typedef struct EdgeFeatReduceData { + int feat_edges; +} EdgeFeatReduceData; + +static void feat_data_sum_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) { - BMLoop *ll, *lr = NULL; + EdgeFeatReduceData *feat_chunk_join = (EdgeFeatReduceData *)chunk_join; + EdgeFeatReduceData *feat_chunk = (EdgeFeatReduceData *)chunk; + feat_chunk_join->feat_edges += feat_chunk->feat_edges; +} - ll = e->l; - if (ll) { - lr = e->l->radial_next; - } +static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict tls) +{ + EdgeFeatData *e_feat_data = (EdgeFeatData *)userdata; + EdgeFeatReduceData *reduce_data = (EdgeFeatReduceData *)tls->userdata_chunk; + Mesh *me = e_feat_data->me; + LineartEdgeNeighbor *edge_nabr = e_feat_data->edge_nabr; + const MLoopTri *mlooptri = e_feat_data->mlooptri; + + uint16_t edge_flag_result = 0; - if (!ll && !lr) { - return LRT_EDGE_FLAG_LOOSE; + /* Because the edge neighbor array contains loop edge pairs, we only need to process the first + * edge in the pair. Otherwise we would add the same edge that the loops represent twice. */ + if (i < edge_nabr[i].e) { + return; } - FreestyleEdge *fel, *fer; bool face_mark_filtered = false; - uint16_t edge_flag_result = 0; + bool enable_face_mark = (e_feat_data->use_freestyle_face && e_feat_data->rb->filter_face_mark); bool only_contour = false; - - if (use_freestyle_face && rb->filter_face_mark) { - fel = CustomData_bmesh_get(&bm_if_freestyle->pdata, ll->f->head.data, CD_FREESTYLE_FACE); - if (ll != lr && lr) { - fer = CustomData_bmesh_get(&bm_if_freestyle->pdata, lr->f->head.data, CD_FREESTYLE_FACE); + if (enable_face_mark) { + FreestyleFace *ff1, *ff2; + int index = e_feat_data->freestyle_face_index; + if (index > -1) { + ff1 = &((FreestyleFace *)me->pdata.layers[index].data)[mlooptri[i / 3].poly]; + } + if (edge_nabr[i].e > -1) { + ff2 = &((FreestyleFace *)me->pdata.layers[index].data)[mlooptri[edge_nabr[i].e / 3].poly]; } else { - /* Handles mesh boundary case */ - fer = fel; + /* Handle mesh boundary cases: We want mesh boundaries to respect + * `filter_face_mark_boundaries` option the same way as face mark boundaries, and the code + * path is simper when it's assuming both ff1 and ff2 not NULL. */ + ff2 = ff1; } - if (rb->filter_face_mark_boundaries ^ rb->filter_face_mark_invert) { - if ((fel->flag & FREESTYLE_FACE_MARK) || (fer->flag & FREESTYLE_FACE_MARK)) { + if (e_feat_data->rb->filter_face_mark_boundaries ^ e_feat_data->rb->filter_face_mark_invert) { + if ((ff1->flag & FREESTYLE_FACE_MARK) || (ff2->flag & FREESTYLE_FACE_MARK)) { face_mark_filtered = true; } } else { - if ((fel->flag & FREESTYLE_FACE_MARK) && (fer->flag & FREESTYLE_FACE_MARK) && (fer != fel)) { + if ((ff1->flag & FREESTYLE_FACE_MARK) && (ff2->flag & FREESTYLE_FACE_MARK) && (ff2 != ff1)) { face_mark_filtered = true; } } - if (rb->filter_face_mark_invert) { + if (e_feat_data->rb->filter_face_mark_invert) { face_mark_filtered = !face_mark_filtered; } if (!face_mark_filtered) { - if (rb->filter_face_mark_keep_contour) { + edge_nabr[i].flags = LRT_EDGE_FLAG_INHIBIT; + if (e_feat_data->rb->filter_face_mark_keep_contour) { only_contour = true; } - else { - return 0; - } } } + if (enable_face_mark && !face_mark_filtered && !only_contour) { + return; + } + /* Mesh boundary */ - if (!lr || ll == lr) { - return (edge_flag_result | LRT_EDGE_FLAG_CONTOUR); + if (edge_nabr[i].e == -1) { + edge_nabr[i].flags = LRT_EDGE_FLAG_CONTOUR; + reduce_data->feat_edges += 1; + return; } LineartTriangle *tri1, *tri2; - LineartVert *l; + LineartVert *vert; + LineartRenderBuffer *rb = e_feat_data->rb; + + int f1 = i / 3, f2 = edge_nabr[i].e / 3; /* The mesh should already be triangulated now, so we can assume each face is a triangle. */ - tri1 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(ll->f)); - tri2 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(lr->f)); + tri1 = lineart_triangle_from_index(rb, e_feat_data->tri_array, f1); + tri2 = lineart_triangle_from_index(rb, e_feat_data->tri_array, f2); - l = &rv_array[BM_elem_index_get(e->v1)]; + vert = &e_feat_data->v_array[edge_nabr[i].v1]; - double vv[3]; - double *view_vector = vv; + double view_vector_persp[3]; + double *view_vector = view_vector_persp; double dot_1 = 0, dot_2 = 0; double result; bool material_back_face = ((tri1->flags | tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING); if (rb->use_contour || rb->use_back_face_culling || material_back_face) { - if (rb->cam_is_persp) { - sub_v3_v3v3_db(view_vector, rb->camera_pos, l->gloc); + sub_v3_v3v3_db(view_vector, rb->camera_pos, vert->gloc); } else { view_vector = rb->view_vector; @@ -1542,11 +1566,10 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, dot_1 = dot_v3v3_db(view_vector, tri1->gn); dot_2 = dot_v3v3_db(view_vector, tri2->gn); - if (rb->use_contour && (result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) { + if ((result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) { edge_flag_result |= LRT_EDGE_FLAG_CONTOUR; } - /* Because the ray points towards the camera, so back-face is when dot value being negative. */ if (rb->use_back_face_culling) { if (dot_1 < 0) { tri1->flags |= LRT_CULL_DISCARD; @@ -1564,127 +1587,177 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, } } } - else { - view_vector = rb->view_vector; - } - if ((result = dot_1 * dot_2) <= 0 && (fabs(dot_1) + fabs(dot_2))) { - edge_flag_result |= LRT_EDGE_FLAG_CONTOUR; - } + if (!only_contour) { - /* For when face mark filtering decided that we discard the face but keep_contour option is on. - * so we still have correct full contour around the object. */ - if (only_contour) { - return edge_flag_result; - } + if (rb->use_crease) { + bool do_crease = true; + if (!rb->force_crease && !e_feat_data->use_auto_smooth && + (me->mpoly[mlooptri[f1].poly].flag & ME_SMOOTH) && + (me->mpoly[mlooptri[f2].poly].flag & ME_SMOOTH)) { + do_crease = false; + } + if (do_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < e_feat_data->crease_threshold)) { + edge_flag_result |= LRT_EDGE_FLAG_CREASE; + } + } + + int mat1 = me->mpoly[mlooptri[f1].poly].mat_nr; + int mat2 = me->mpoly[mlooptri[f2].poly].mat_nr; - /* Do not show lines other than contour on back face (because contour has one adjacent face that - * isn't a back face). - * TODO(Yiming): Do we need separate option for this? */ - if (rb->use_back_face_culling || - ((tri1->flags & tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING)) { - if (dot_1 < 0 && dot_2 < 0) { - return edge_flag_result; + if (rb->use_material && mat1 != mat2) { + edge_flag_result |= LRT_EDGE_FLAG_MATERIAL; } } + else { /* only_contour */ + if (!edge_flag_result) { /* Other edge types inhibited */ + return; + } + } + + int real_edges[3]; + BKE_mesh_looptri_get_real_edges(me, &mlooptri[i / 3], real_edges); - if (rb->use_crease) { - if (rb->sharp_as_crease && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) { + if (real_edges[i % 3] >= 0) { + MEdge *medge = &me->medge[real_edges[i % 3]]; + + if (rb->use_crease && rb->sharp_as_crease && (medge->flag & ME_SHARP)) { edge_flag_result |= LRT_EDGE_FLAG_CREASE; } - else { - bool do_crease = true; - if (!rb->force_crease && !use_auto_smooth && - (BM_elem_flag_test(ll->f, BM_ELEM_SMOOTH) && BM_elem_flag_test(lr->f, BM_ELEM_SMOOTH))) { - do_crease = false; - } - if (do_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < crease_threshold)) { - edge_flag_result |= LRT_EDGE_FLAG_CREASE; + + if (rb->use_edge_marks && e_feat_data->use_freestyle_edge) { + FreestyleEdge *fe; + int index = e_feat_data->freestyle_edge_index; + fe = &((FreestyleEdge *)me->edata.layers[index].data)[real_edges[i % 3]]; + if (fe->flag & FREESTYLE_EDGE_MARK) { + edge_flag_result |= LRT_EDGE_FLAG_EDGE_MARK; } } } - if (rb->use_material && (ll->f->mat_nr != lr->f->mat_nr)) { - edge_flag_result |= LRT_EDGE_FLAG_MATERIAL; + + edge_nabr[i].flags = edge_flag_result; + + if (edge_flag_result) { + /* Only allocate for feature edge (instead of all edges) to save memory. + * If allow duplicated edges, one edge gets added multiple times if it has multiple types. + */ + reduce_data->feat_edges += e_feat_data->rb->allow_duplicated_types ? + lineart_edge_type_duplication_count(edge_flag_result) : + 1; } - if (use_freestyle_edge && rb->use_edge_marks) { - FreestyleEdge *fe; - fe = CustomData_bmesh_get(&bm_if_freestyle->edata, e->head.data, CD_FREESTYLE_EDGE); - if (fe->flag & FREESTYLE_EDGE_MARK) { - edge_flag_result |= LRT_EDGE_FLAG_EDGE_MARK; - } +} + +typedef struct LooseEdgeData { + int loose_count; + int loose_max; + MEdge **loose_array; + Mesh *me; +} LooseEdgeData; + +static void lineart_loose_data_reallocate(LooseEdgeData *loose_data, int count) +{ + MEdge **new_arr = MEM_callocN(sizeof(MEdge *) * count, "loose edge array"); + if (loose_data->loose_array) { + memcpy(new_arr, loose_data->loose_array, sizeof(MEdge *) * loose_data->loose_max); + MEM_freeN(loose_data->loose_array); } - return edge_flag_result; + loose_data->loose_max = count; + loose_data->loose_array = new_arr; } -static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e) +static void lineart_join_loose_edge_arr(LooseEdgeData *loose_data, LooseEdgeData *to_be_joined) { - switch (e->flags) { - case LRT_EDGE_FLAG_CONTOUR: - lineart_prepend_edge_direct(&rb->contour.first, e); - break; - case LRT_EDGE_FLAG_CREASE: - lineart_prepend_edge_direct(&rb->crease.first, e); - break; - case LRT_EDGE_FLAG_MATERIAL: - lineart_prepend_edge_direct(&rb->material.first, e); - break; - case LRT_EDGE_FLAG_EDGE_MARK: - lineart_prepend_edge_direct(&rb->edge_mark.first, e); - break; - case LRT_EDGE_FLAG_INTERSECTION: - lineart_prepend_edge_direct(&rb->intersection.first, e); - break; - case LRT_EDGE_FLAG_LOOSE: - lineart_prepend_edge_direct(&rb->floating.first, e); - break; + if (!to_be_joined->loose_array) { + return; } + int new_count = loose_data->loose_count + to_be_joined->loose_count; + if (new_count >= loose_data->loose_max) { + lineart_loose_data_reallocate(loose_data, new_count); + } + memcpy(&loose_data->loose_array[loose_data->loose_count], + to_be_joined->loose_array, + sizeof(MEdge *) * to_be_joined->loose_count); + loose_data->loose_count += to_be_joined->loose_count; + MEM_freeN(to_be_joined->loose_array); +} + +static void lineart_add_loose_edge(LooseEdgeData *loose_data, MEdge *e) +{ + if (loose_data->loose_count >= loose_data->loose_max) { + int min_amount = MAX2(100, loose_data->loose_count * 2); + lineart_loose_data_reallocate(loose_data, min_amount); + } + loose_data->loose_array[loose_data->loose_count] = e; + loose_data->loose_count++; } -static void lineart_add_edge_to_list_thread(LineartObjectInfo *obi, LineartEdge *e) +static void lineart_identify_loose_edges(void *__restrict UNUSED(userdata), + const int i, + const TaskParallelTLS *__restrict tls) { + LooseEdgeData *loose_data = (LooseEdgeData *)tls->userdata_chunk; + Mesh *me = loose_data->me; -#define LRT_ASSIGN_EDGE(name) \ - lineart_prepend_edge_direct(&obi->name.first, e); \ - if (!obi->name.last) { \ - obi->name.last = e; \ + if (me->medge[i].flag & ME_LOOSEEDGE) { + lineart_add_loose_edge(loose_data, &me->medge[i]); } - switch (e->flags) { - case LRT_EDGE_FLAG_CONTOUR: - LRT_ASSIGN_EDGE(contour); - break; - case LRT_EDGE_FLAG_CREASE: - LRT_ASSIGN_EDGE(crease); - break; - case LRT_EDGE_FLAG_MATERIAL: - LRT_ASSIGN_EDGE(material); - break; - case LRT_EDGE_FLAG_EDGE_MARK: - LRT_ASSIGN_EDGE(edge_mark); - break; - case LRT_EDGE_FLAG_INTERSECTION: - LRT_ASSIGN_EDGE(intersection); - break; - case LRT_EDGE_FLAG_LOOSE: - LRT_ASSIGN_EDGE(floating); - break; +} + +static void loose_data_sum_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + LooseEdgeData *final = (LooseEdgeData *)chunk_join; + LooseEdgeData *loose_chunk = (LooseEdgeData *)chunk; + lineart_join_loose_edge_arr(final, loose_chunk); +} + +static void lineart_add_edge_to_array(LineartPendingEdges *pe, LineartEdge *e) +{ + if (pe->next >= pe->max || !pe->max) { + if (!pe->max) { + pe->max = 1000; + } + + LineartEdge **new_array = MEM_mallocN(sizeof(LineartEdge *) * pe->max * 2, + "LineartPendingEdges array"); + if (LIKELY(pe->array)) { + memcpy(new_array, pe->array, sizeof(LineartEdge *) * pe->max); + MEM_freeN(pe->array); + } + pe->max *= 2; + pe->array = new_array; + } + pe->array[pe->next] = e; + pe->next++; +} + +static void lineart_add_edge_to_array_thread(LineartObjectInfo *obi, LineartEdge *e) +{ + lineart_add_edge_to_array(&obi->pending_edges, e); +} + +/* Note: For simplicity, this function doesn't actually do anything if you already have data in + * #pe. */ +static void lineart_finalize_object_edge_array_reserve(LineartPendingEdges *pe, int count) +{ + if (pe->max || pe->array) { + return; } -#undef LRT_ASSIGN_EDGE + + pe->max = count; + LineartEdge **new_array = MEM_mallocN(sizeof(LineartEdge *) * pe->max, + "LineartPendingEdges array final"); + pe->array = new_array; } -static void lineart_finalize_object_edge_list(LineartRenderBuffer *rb, LineartObjectInfo *obi) +static void lineart_finalize_object_edge_array(LineartPendingEdges *pe, LineartObjectInfo *obi) { -#define LRT_OBI_TO_RB(name) \ - if (obi->name.last) { \ - ((LineartEdge *)obi->name.last)->next = rb->name.first; \ - rb->name.first = obi->name.first; \ - } - LRT_OBI_TO_RB(contour); - LRT_OBI_TO_RB(crease); - LRT_OBI_TO_RB(material); - LRT_OBI_TO_RB(edge_mark); - LRT_OBI_TO_RB(intersection); - LRT_OBI_TO_RB(floating); -#undef LRT_OBI_TO_RB + memcpy(&pe->array[pe->next], + obi->pending_edges.array, + sizeof(LineartEdge *) * obi->pending_edges.next); + MEM_freeN(obi->pending_edges.array); + pe->next += obi->pending_edges.next; } static void lineart_triangle_adjacent_assign(LineartTriangle *tri, @@ -1702,298 +1775,416 @@ static void lineart_triangle_adjacent_assign(LineartTriangle *tri, } } -static int lineart_edge_type_duplication_count(char eflag) +typedef struct TriData { + LineartObjectInfo *ob_info; + const MLoopTri *mlooptri; + LineartVert *vert_arr; + LineartTriangle *tri_arr; + int lineart_triangle_size; + LineartTriangleAdjacent *tri_adj; +} TriData; + +static void lineart_load_tri_task(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict UNUSED(tls)) { - int count = 0; - /* See eLineartEdgeFlag for details. */ - for (int i = 0; i < 6; i++) { - if (eflag & (1 << i)) { - count++; - } - } - return count; + TriData *tri_task_data = (TriData *)userdata; + Mesh *me = tri_task_data->ob_info->original_me; + LineartObjectInfo *ob_info = tri_task_data->ob_info; + const MLoopTri *mlooptri = &tri_task_data->mlooptri[i]; + LineartVert *vert_arr = tri_task_data->vert_arr; + LineartTriangle *tri = tri_task_data->tri_arr; + + tri = (LineartTriangle *)(((uchar *)tri) + tri_task_data->lineart_triangle_size * i); + + int v1 = me->mloop[mlooptri->tri[0]].v; + int v2 = me->mloop[mlooptri->tri[1]].v; + int v3 = me->mloop[mlooptri->tri[2]].v; + + tri->v[0] = &vert_arr[v1]; + tri->v[1] = &vert_arr[v2]; + tri->v[2] = &vert_arr[v3]; + + /* Material mask bits and occlusion effectiveness assignment. */ + Material *mat = BKE_object_material_get(ob_info->original_ob, + me->mpoly[mlooptri->poly].mat_nr + 1); + tri->material_mask_bits |= ((mat && (mat->lineart.flags & LRT_MATERIAL_MASK_ENABLED)) ? + mat->lineart.material_mask_bits : + 0); + tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1); + tri->flags |= (mat && (mat->blend_flag & MA_BL_CULL_BACKFACE)) ? + LRT_TRIANGLE_MAT_BACK_FACE_CULLING : + 0; + + tri->intersection_mask = ob_info->override_intersection_mask; + + double gn[3]; + float no[3]; + normal_tri_v3(no, me->mvert[v1].co, me->mvert[v2].co, me->mvert[v3].co); + copy_v3db_v3fl(gn, no); + mul_v3_mat3_m4v3_db(tri->gn, ob_info->normal, gn); + normalize_v3_db(tri->gn); + + if (ob_info->usage == OBJECT_LRT_INTERSECTION_ONLY) { + tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY; + } + else if (ob_info->usage == OBJECT_LRT_NO_INTERSECTION || + ob_info->usage == OBJECT_LRT_OCCLUSION_ONLY) { + tri->flags |= LRT_TRIANGLE_NO_INTERSECTION; + } + + /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */ + tri->intersecting_verts = (void *)&tri_task_data->tri_adj[i]; } -static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBuffer *rb) +typedef struct EdgeNeighborData { + LineartEdgeNeighbor *edge_nabr; + LineartAdjacentEdge *adj_e; + MLoopTri *mlooptri; + MLoop *mloop; +} EdgeNeighborData; + +static void lineart_edge_neighbor_init_task(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict UNUSED(tls)) { - BMesh *bm; - BMVert *v; - BMFace *f; - BMEdge *e; - BMLoop *loop; - LineartEdge *la_e; - LineartEdgeSegment *la_s; - LineartTriangle *tri; - LineartTriangleAdjacent *orta; - double(*model_view_proj)[4] = obi->model_view_proj, (*model_view)[4] = obi->model_view, - (*normal)[4] = obi->normal; - LineartElementLinkNode *eln; - LineartVert *orv; - LineartEdge *o_la_e; - LineartEdgeSegment *o_la_s; - LineartTriangle *ort; - Object *orig_ob; - bool can_find_freestyle_edge = false; - bool can_find_freestyle_face = false; - int i; - float use_crease = 0; + EdgeNeighborData *en_data = (EdgeNeighborData *)userdata; + LineartAdjacentEdge *adj_e = &en_data->adj_e[i]; + MLoopTri *looptri = &en_data->mlooptri[i / 3]; + LineartEdgeNeighbor *edge_nabr = &en_data->edge_nabr[i]; + MLoop *mloop = en_data->mloop; + + adj_e->e = i; + adj_e->v1 = mloop[looptri->tri[i % 3]].v; + adj_e->v2 = mloop[looptri->tri[(i + 1) % 3]].v; + if (adj_e->v1 > adj_e->v2) { + SWAP(unsigned int, adj_e->v1, adj_e->v2); + } + edge_nabr->e = -1; + + edge_nabr->v1 = adj_e->v1; + edge_nabr->v2 = adj_e->v2; + edge_nabr->flags = 0; +} - int usage = obi->usage; +static LineartEdgeNeighbor *lineart_build_edge_neighbor(Mesh *me, int total_edges) +{ + /* Because the mesh is triangulated, so `me->totedge` should be reliable? */ + LineartAdjacentEdge *adj_e = MEM_mallocN(sizeof(LineartAdjacentEdge) * total_edges, + "LineartAdjacentEdge arr"); + LineartEdgeNeighbor *edge_nabr = MEM_mallocN(sizeof(LineartEdgeNeighbor) * total_edges, + "LineartEdgeNeighbor arr"); - if (obi->original_me->edit_mesh) { - /* Do not use edit_mesh directly because we will modify it, so create a copy. */ - bm = BM_mesh_copy(obi->original_me->edit_mesh->bm); - } - else { - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(((Mesh *)(obi->original_me))); - bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = true, - })); - BM_mesh_bm_from_me(bm, - obi->original_me, - &((struct BMeshFromMeshParams){ - .calc_face_normal = true, - .calc_vert_normal = true, - })); - } + MLoopTri *mlooptri = me->runtime.looptris.array; - if (obi->free_use_mesh) { - BKE_id_free(NULL, obi->original_me); - } + TaskParallelSettings en_settings; + BLI_parallel_range_settings_defaults(&en_settings); + /* Set the minimum amount of edges a thread has to process. */ + en_settings.min_iter_per_thread = 50000; + + EdgeNeighborData en_data; + en_data.adj_e = adj_e; + en_data.edge_nabr = edge_nabr; + en_data.mlooptri = mlooptri; + en_data.mloop = me->mloop; + + BLI_task_parallel_range(0, total_edges, &en_data, lineart_edge_neighbor_init_task, &en_settings); - if (rb->remove_doubles) { - BMEditMesh *em = BKE_editmesh_create(bm); - BMOperator findop, weldop; + lineart_sort_adjacent_items(adj_e, total_edges); + + for (int i = 0; i < total_edges - 1; i++) { + if (adj_e[i].v1 == adj_e[i + 1].v1 && adj_e[i].v2 == adj_e[i + 1].v2) { + edge_nabr[adj_e[i].e].e = adj_e[i + 1].e; + edge_nabr[adj_e[i + 1].e].e = adj_e[i].e; + } + } - /* See bmesh_opdefines.c and bmesh_operators.c for op names and argument formatting. */ - BMO_op_initf(bm, &findop, BMO_FLAG_DEFAULTS, "find_doubles verts=%av dist=%f", 0.0001); + MEM_freeN(adj_e); - BMO_op_exec(bm, &findop); + return edge_nabr; +} - /* Weld the vertices. */ - BMO_op_init(bm, &weldop, BMO_FLAG_DEFAULTS, "weld_verts"); - BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap"); - BMO_op_exec(bm, &weldop); +static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRenderBuffer *re_buf) +{ + LineartElementLinkNode *elem_link_node; + LineartVert *la_v_arr; + LineartEdge *la_edge_arr; + LineartEdgeSegment *la_seg_arr; + LineartTriangle *la_tri_arr; - BMO_op_finish(bm, &findop); - BMO_op_finish(bm, &weldop); + Mesh *me = ob_info->original_me; - MEM_freeN(em); + if (!me->totedge) { + return; } - BM_mesh_elem_hflag_disable_all(bm, BM_FACE | BM_EDGE, BM_ELEM_TAG, false); - BM_mesh_triangulate( - bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_BEAUTY, 4, false, NULL, NULL, NULL); - BM_mesh_normals_update(bm); - BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); - BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + /* Triangulate. */ + const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(me); + const int tot_tri = BKE_mesh_runtime_looptri_len(me); - if (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) { - can_find_freestyle_edge = 1; + /* Check if we should look for custom data tags like Freestyle edges or faces. */ + bool can_find_freestyle_edge = false; + int layer_index = CustomData_get_active_layer_index(&me->edata, CD_FREESTYLE_EDGE); + if (layer_index != -1) { + can_find_freestyle_edge = true; } - if (CustomData_has_layer(&bm->pdata, CD_FREESTYLE_FACE)) { + + bool can_find_freestyle_face = false; + layer_index = CustomData_get_active_layer_index(&me->pdata, CD_FREESTYLE_FACE); + if (layer_index != -1) { can_find_freestyle_face = true; } /* If we allow duplicated edges, one edge should get added multiple times if is has been * classified as more than one edge type. This is so we can create multiple different line type * chains containing the same edge. */ - orv = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartVert) * bm->totvert); - ort = lineart_mem_acquire_thread(&rb->render_data_pool, bm->totface * rb->triangle_size); + la_v_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + sizeof(LineartVert) * me->totvert); + la_tri_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + tot_tri * re_buf->triangle_size); - orig_ob = obi->original_ob; + Object *orig_ob = ob_info->original_ob; - BLI_spin_lock(&rb->lock_task); - eln = lineart_list_append_pointer_pool_sized_thread( - &rb->vertex_buffer_pointers, &rb->render_data_pool, orv, sizeof(LineartElementLinkNode)); - BLI_spin_unlock(&rb->lock_task); + BLI_spin_lock(&re_buf->lock_task); + elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->vertex_buffer_pointers, + &re_buf->render_data_pool, + la_v_arr, + sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&re_buf->lock_task); - eln->element_count = bm->totvert; - eln->object_ref = orig_ob; - obi->v_eln = eln; + elem_link_node->element_count = me->totvert; + elem_link_node->object_ref = orig_ob; + ob_info->v_eln = elem_link_node; bool use_auto_smooth = false; + float crease_angle = 0; if (orig_ob->lineart.flags & OBJECT_LRT_OWN_CREASE) { - use_crease = cosf(M_PI - orig_ob->lineart.crease_threshold); + crease_angle = cosf(M_PI - orig_ob->lineart.crease_threshold); } - else if (obi->original_me->flag & ME_AUTOSMOOTH) { - use_crease = cosf(obi->original_me->smoothresh); + else if (ob_info->original_me->flag & ME_AUTOSMOOTH) { + crease_angle = cosf(ob_info->original_me->smoothresh); use_auto_smooth = true; } else { - use_crease = rb->crease_threshold; + crease_angle = re_buf->crease_threshold; } /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates * erroneous detection on creases. Future configuration should allow options. */ if (orig_ob->type == OB_FONT) { - eln->flags |= LRT_ELEMENT_BORDER_ONLY; + elem_link_node->flags |= LRT_ELEMENT_BORDER_ONLY; } - BLI_spin_lock(&rb->lock_task); - eln = lineart_list_append_pointer_pool_sized_thread( - &rb->triangle_buffer_pointers, &rb->render_data_pool, ort, sizeof(LineartElementLinkNode)); - BLI_spin_unlock(&rb->lock_task); + BLI_spin_lock(&re_buf->lock_task); + elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->triangle_buffer_pointers, + &re_buf->render_data_pool, + la_tri_arr, + sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&re_buf->lock_task); - eln->element_count = bm->totface; - eln->object_ref = orig_ob; - eln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0); + int usage = ob_info->usage; + + elem_link_node->element_count = tot_tri; + elem_link_node->object_ref = orig_ob; + elem_link_node->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0); /* Note this memory is not from pool, will be deleted after culling. */ - orta = MEM_callocN(sizeof(LineartTriangleAdjacent) * bm->totface, "LineartTriangleAdjacent"); + LineartTriangleAdjacent *tri_adj = MEM_callocN(sizeof(LineartTriangleAdjacent) * tot_tri, + "LineartTriangleAdjacent"); /* Link is minimal so we use pool anyway. */ - BLI_spin_lock(&rb->lock_task); + BLI_spin_lock(&re_buf->lock_task); lineart_list_append_pointer_pool_thread( - &rb->triangle_adjacent_pointers, &rb->render_data_pool, orta); - BLI_spin_unlock(&rb->lock_task); - - for (i = 0; i < bm->totvert; i++) { - v = BM_vert_at_index(bm, i); - lineart_vert_transform(v, i, orv, model_view, model_view_proj); - orv[i].index = i; - } - /* Register a global index increment. See #lineart_triangle_share_edge() and - * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might eventually - * overflow, in such large scene it's virtually impossible for two vertex of the same numeric - * index to come close together. */ - obi->global_i_offset = bm->totvert; - - tri = ort; - for (i = 0; i < bm->totface; i++) { - f = BM_face_at_index(bm, i); - - loop = f->l_first; - tri->v[0] = &orv[BM_elem_index_get(loop->v)]; - loop = loop->next; - tri->v[1] = &orv[BM_elem_index_get(loop->v)]; - loop = loop->next; - tri->v[2] = &orv[BM_elem_index_get(loop->v)]; - - /* Material mask bits and occlusion effectiveness assignment. */ - Material *mat = BKE_object_material_get(orig_ob, f->mat_nr + 1); - tri->material_mask_bits |= ((mat && (mat->lineart.flags & LRT_MATERIAL_MASK_ENABLED)) ? - mat->lineart.material_mask_bits : - 0); - tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1); - tri->flags |= (mat && (mat->blend_flag & MA_BL_CULL_BACKFACE)) ? - LRT_TRIANGLE_MAT_BACK_FACE_CULLING : - 0; - - tri->intersection_mask = obi->override_intersection_mask; - - double gn[3]; - copy_v3db_v3fl(gn, f->no); - mul_v3_mat3_m4v3_db(tri->gn, normal, gn); - normalize_v3_db(tri->gn); - - if (usage == OBJECT_LRT_INTERSECTION_ONLY) { - tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY; - } - else if (ELEM(usage, OBJECT_LRT_NO_INTERSECTION, OBJECT_LRT_OCCLUSION_ONLY)) { - tri->flags |= LRT_TRIANGLE_NO_INTERSECTION; - } - - /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */ - tri->intersecting_verts = (void *)&orta[i]; - - tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size); - } - - /* Use BM_ELEM_TAG in f->head.hflag to store needed faces in the first iteration. */ - - int allocate_la_e = 0; - for (i = 0; i < bm->totedge; i++) { - e = BM_edge_at_index(bm, i); - - /* Because e->head.hflag is char, so line type flags should not exceed positive 7 bits. */ - uint16_t eflag = lineart_identify_feature_line(rb, - e, - ort, - orv, - use_crease, - use_auto_smooth, - can_find_freestyle_edge, - can_find_freestyle_face, - bm); - if (eflag) { - /* Only allocate for feature lines (instead of all lines) to save memory. - * If allow duplicated edges, one edge gets added multiple times if it has multiple types. - */ - allocate_la_e += rb->allow_duplicated_types ? lineart_edge_type_duplication_count(eflag) : 1; + &re_buf->triangle_adjacent_pointers, &re_buf->render_data_pool, tri_adj); + BLI_spin_unlock(&re_buf->lock_task); + + /* Convert all vertices to lineart verts. */ + TaskParallelSettings vert_settings; + BLI_parallel_range_settings_defaults(&vert_settings); + /* Set the minimum amount of verts a thread has to process. */ + vert_settings.min_iter_per_thread = 4000; + + VertData vert_data; + vert_data.mvert = me->mvert; + vert_data.v_arr = la_v_arr; + vert_data.model_view = ob_info->model_view; + vert_data.model_view_proj = ob_info->model_view_proj; + + BLI_task_parallel_range( + 0, me->totvert, &vert_data, lineart_mvert_transform_task, &vert_settings); + + /* Convert all mesh triangles into lineart triangles. + * Also create an edge map to get connectivity between edges and triangles. */ + TaskParallelSettings tri_settings; + BLI_parallel_range_settings_defaults(&tri_settings); + /* Set the minimum amount of triangles a thread has to process. */ + tri_settings.min_iter_per_thread = 4000; + + TriData tri_data; + tri_data.ob_info = ob_info; + tri_data.mlooptri = mlooptri; + tri_data.vert_arr = la_v_arr; + tri_data.tri_arr = la_tri_arr; + tri_data.lineart_triangle_size = re_buf->triangle_size; + tri_data.tri_adj = tri_adj; + + unsigned int total_edges = tot_tri * 3; + + BLI_task_parallel_range(0, tot_tri, &tri_data, lineart_load_tri_task, &tri_settings); + + /* Check for contour lines in the mesh. + * IE check if the triangle edges lies in area where the triangles go from front facing to back + * facing. + */ + EdgeFeatReduceData edge_reduce = {0}; + TaskParallelSettings edge_feat_settings; + BLI_parallel_range_settings_defaults(&edge_feat_settings); + /* Set the minimum amount of edges a thread has to process. */ + edge_feat_settings.min_iter_per_thread = 4000; + edge_feat_settings.userdata_chunk = &edge_reduce; + edge_feat_settings.userdata_chunk_size = sizeof(EdgeFeatReduceData); + edge_feat_settings.func_reduce = feat_data_sum_reduce; + + EdgeFeatData edge_feat_data = {0}; + edge_feat_data.rb = re_buf; + edge_feat_data.me = me; + edge_feat_data.mlooptri = mlooptri; + edge_feat_data.edge_nabr = lineart_build_edge_neighbor(me, total_edges); + edge_feat_data.tri_array = la_tri_arr; + edge_feat_data.v_array = la_v_arr; + edge_feat_data.crease_threshold = crease_angle; + edge_feat_data.use_auto_smooth = use_auto_smooth; + edge_feat_data.use_freestyle_face = can_find_freestyle_face; + edge_feat_data.use_freestyle_edge = can_find_freestyle_edge; + if (edge_feat_data.use_freestyle_face) { + edge_feat_data.freestyle_face_index = CustomData_get_layer_index(&me->pdata, + CD_FREESTYLE_FACE); + } + if (edge_feat_data.use_freestyle_edge) { + edge_feat_data.freestyle_edge_index = CustomData_get_layer_index(&me->edata, + CD_FREESTYLE_EDGE); + } + + BLI_task_parallel_range(0, + total_edges, + &edge_feat_data, + lineart_identify_mlooptri_feature_edges, + &edge_feat_settings); + + LooseEdgeData loose_data = {0}; + if (re_buf->use_loose) { + /* Only identifying floating edges at this point because other edges has been taken care of + * inside #lineart_identify_mlooptri_feature_edges function. */ + TaskParallelSettings edge_loose_settings; + BLI_parallel_range_settings_defaults(&edge_loose_settings); + edge_loose_settings.min_iter_per_thread = 4000; + edge_loose_settings.func_reduce = loose_data_sum_reduce; + edge_loose_settings.userdata_chunk = &loose_data; + edge_loose_settings.userdata_chunk_size = sizeof(LooseEdgeData); + loose_data.me = me; + BLI_task_parallel_range( + 0, me->totedge, &loose_data, lineart_identify_loose_edges, &edge_loose_settings); + } + + int allocate_la_e = edge_reduce.feat_edges + loose_data.loose_count; + + la_edge_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + sizeof(LineartEdge) * allocate_la_e); + la_seg_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + sizeof(LineartEdgeSegment) * allocate_la_e); + BLI_spin_lock(&re_buf->lock_task); + elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->line_buffer_pointers, + &re_buf->render_data_pool, + la_edge_arr, + sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&re_buf->lock_task); + elem_link_node->element_count = allocate_la_e; + elem_link_node->object_ref = orig_ob; + + // Start of the edge/seg arr + LineartEdge *la_edge; + LineartEdgeSegment *la_seg; + la_edge = la_edge_arr; + la_seg = la_seg_arr; + + for (int i = 0; i < total_edges; i++) { + LineartEdgeNeighbor *edge_nabr = &edge_feat_data.edge_nabr[i]; + + if (i < edge_nabr->e) { + continue; } - /* Here we just use bm's flag for when loading actual lines, then we don't need to call - * lineart_identify_feature_line() again, e->head.hflag deleted after loading anyway. Always - * set the flag, so hflag stays 0 for lines that are not feature lines. */ - e->head.hflag = eflag; - } - - o_la_e = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e); - o_la_s = lineart_mem_acquire_thread(&rb->render_data_pool, - sizeof(LineartEdgeSegment) * allocate_la_e); - BLI_spin_lock(&rb->lock_task); - eln = lineart_list_append_pointer_pool_sized_thread( - &rb->line_buffer_pointers, &rb->render_data_pool, o_la_e, sizeof(LineartElementLinkNode)); - BLI_spin_unlock(&rb->lock_task); - eln->element_count = allocate_la_e; - eln->object_ref = orig_ob; - - la_e = o_la_e; - la_s = o_la_s; - for (i = 0; i < bm->totedge; i++) { - e = BM_edge_at_index(bm, i); /* Not a feature line, so we skip. */ - if (!e->head.hflag) { + if (edge_nabr->flags == 0) { continue; } - bool edge_added = false; + LineartEdge *edge_added = NULL; /* See eLineartEdgeFlag for details. */ - for (int flag_bit = 0; flag_bit < 6; flag_bit++) { + for (int flag_bit = 0; flag_bit < LRT_EDGE_FLAG_TYPE_MAX_BITS; flag_bit++) { char use_type = 1 << flag_bit; - if (!(use_type & e->head.hflag)) { + if (!(use_type & edge_nabr->flags)) { continue; } - la_e->v1 = &orv[BM_elem_index_get(e->v1)]; - la_e->v2 = &orv[BM_elem_index_get(e->v2)]; - la_e->v1_obindex = la_e->v1->index; - la_e->v2_obindex = la_e->v2->index; - if (e->l) { - int findex = BM_elem_index_get(e->l->f); - la_e->t1 = lineart_triangle_from_index(rb, ort, findex); + la_edge->v1 = &la_v_arr[edge_nabr->v1]; + la_edge->v2 = &la_v_arr[edge_nabr->v2]; + int findex = i / 3; + la_edge->t1 = lineart_triangle_from_index(re_buf, la_tri_arr, findex); + if (!edge_added) { + lineart_triangle_adjacent_assign(la_edge->t1, &tri_adj[findex], la_edge); + } + if (edge_nabr->e != -1) { + findex = edge_nabr->e / 3; + la_edge->t2 = lineart_triangle_from_index(re_buf, la_tri_arr, findex); if (!edge_added) { - lineart_triangle_adjacent_assign(la_e->t1, &orta[findex], la_e); - } - if (e->l->radial_next && e->l->radial_next != e->l) { - findex = BM_elem_index_get(e->l->radial_next->f); - la_e->t2 = lineart_triangle_from_index(rb, ort, findex); - if (!edge_added) { - lineart_triangle_adjacent_assign(la_e->t2, &orta[findex], la_e); - } + lineart_triangle_adjacent_assign(la_edge->t2, &tri_adj[findex], la_edge); } } - la_e->flags = use_type; - la_e->object_ref = orig_ob; - BLI_addtail(&la_e->segments, la_s); - if (ELEM(usage, OBJECT_LRT_INHERIT, OBJECT_LRT_INCLUDE, OBJECT_LRT_NO_INTERSECTION)) { - lineart_add_edge_to_list_thread(obi, la_e); + la_edge->flags = use_type; + la_edge->object_ref = orig_ob; + BLI_addtail(&la_edge->segments, la_seg); + if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE || + usage == OBJECT_LRT_NO_INTERSECTION) { + lineart_add_edge_to_array_thread(ob_info, la_edge); + } + + if (edge_added) { + edge_added->flags |= LRT_EDGE_FLAG_NEXT_IS_DUPLICATION; } - edge_added = true; + edge_added = la_edge; - la_e++; - la_s++; + la_edge++; + la_seg++; - if (!rb->allow_duplicated_types) { + if (!re_buf->allow_duplicated_types) { break; } } } - /* always free bm as it's a copy from before threading */ - BM_mesh_free(bm); + if (loose_data.loose_array) { + for (int i = 0; i < loose_data.loose_count; i++) { + la_edge->v1 = &la_v_arr[loose_data.loose_array[i]->v1]; + la_edge->v2 = &la_v_arr[loose_data.loose_array[i]->v2]; + la_edge->flags = LRT_EDGE_FLAG_LOOSE; + la_edge->object_ref = orig_ob; + BLI_addtail(&la_edge->segments, la_seg); + if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE || + usage == OBJECT_LRT_NO_INTERSECTION) { + lineart_add_edge_to_array_thread(ob_info, la_edge); + } + la_edge++; + la_seg++; + } + MEM_freeN(loose_data.loose_array); + } + + MEM_freeN(edge_feat_data.edge_nabr); + + if (ob_info->free_use_mesh) { + BKE_id_free(NULL, me); + } } static void lineart_object_load_worker(TaskPool *__restrict UNUSED(pool), @@ -2001,6 +2192,9 @@ static void lineart_object_load_worker(TaskPool *__restrict UNUSED(pool), { for (LineartObjectInfo *obi = olti->pending; obi; obi = obi->next) { lineart_geometry_object_load(obi, olti->rb); + if (G.debug_value == 4000) { + printf("thread id: %d processed: %d\n", olti->thread_id, obi->original_me->totpoly); + } } } @@ -2085,6 +2279,7 @@ static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_ use_olti = &olti_list[i]; } } + use_olti->total_faces += this_face_count; obi->next = use_olti->pending; use_olti->pending = obi; @@ -2093,18 +2288,21 @@ static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_ static bool lineart_geometry_check_visible(double (*model_view_proj)[4], double shift_x, double shift_y, - Object *use_ob) + Mesh *use_mesh) { - const BoundBox *bb = BKE_object_boundbox_get(use_ob); - if (!bb) { - /* For lights and empty stuff there will be no bbox. */ + if (!use_mesh) { return false; } + float mesh_min[3], mesh_max[3]; + INIT_MINMAX(mesh_min, mesh_max); + BKE_mesh_minmax(use_mesh, mesh_min, mesh_max); + BoundBox bb = {0}; + BKE_boundbox_init_from_minmax(&bb, mesh_min, mesh_max); double co[8][4]; double tmp[3]; for (int i = 0; i < 8; i++) { - copy_v3db_v3fl(co[i], bb->vec[i]); + copy_v3db_v3fl(co[i], bb.vec[i]); copy_v3_v3_db(tmp, co[i]); mul_v4_m4v3_db(co[i], model_view_proj, tmp); co[i][0] -= shift_x * 2 * co[i][3]; @@ -2130,6 +2328,64 @@ static bool lineart_geometry_check_visible(double (*model_view_proj)[4], return true; } +static void lineart_object_load_single_instance(LineartRenderBuffer *rb, + Depsgraph *depsgraph, + Scene *scene, + Object *ob, + Object *ref_ob, + float use_mat[4][4], + bool is_render, + LineartObjectLoadTaskInfo *olti, + int thread_count) +{ + LineartObjectInfo *obi = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartObjectInfo)); + obi->usage = lineart_usage_check(scene->master_collection, ob, is_render); + obi->override_intersection_mask = lineart_intersection_mask_check(scene->master_collection, ob); + Mesh *use_mesh; + + if (obi->usage == OBJECT_LRT_EXCLUDE) { + return; + } + + /* Prepare the matrix used for transforming this specific object (instance). This has to be + * done before mesh boundbox check because the function needs that. */ + mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, rb->view_projection, use_mat); + mul_m4db_m4db_m4fl_uniq(obi->model_view, rb->view, use_mat); + + if (!ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { + return; + } + if (ob->type == OB_MESH) { + use_mesh = BKE_object_get_evaluated_mesh(ob); + } + else { + use_mesh = BKE_mesh_new_from_object(depsgraph, ob, true, true); + } + + /* In case we still can not get any mesh geometry data from the object */ + if (!use_mesh) { + return; + } + + if (!lineart_geometry_check_visible(obi->model_view_proj, rb->shift_x, rb->shift_y, use_mesh)) { + return; + } + + if (ob->type != OB_MESH) { + obi->free_use_mesh = true; + } + + /* Make normal matrix. */ + float imat[4][4]; + invert_m4_m4(imat, use_mat); + transpose_m4(imat); + copy_m4d_m4(obi->normal, imat); + + obi->original_me = use_mesh; + obi->original_ob = (ref_ob->id.orig_id ? (Object *)ref_ob->id.orig_id : (Object *)ref_ob); + lineart_geometry_load_assign_thread(olti, obi, thread_count, use_mesh->totpoly); +} + static void lineart_main_load_geometries( Depsgraph *depsgraph, Scene *scene, @@ -2178,14 +2434,6 @@ static void lineart_main_load_geometries( BLI_listbase_clear(&rb->triangle_buffer_pointers); BLI_listbase_clear(&rb->vertex_buffer_pointers); - int flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET | - DEG_ITER_OBJECT_FLAG_VISIBLE; - - /* Instance duplicated & particles. */ - if (allow_duplicates) { - flags |= DEG_ITER_OBJECT_FLAG_DUPLI; - } - int thread_count = rb->thread_count; /* This memory is in render buffer memory pool. so we don't need to free those after loading. @@ -2193,75 +2441,43 @@ static void lineart_main_load_geometries( LineartObjectLoadTaskInfo *olti = lineart_mem_acquire( &rb->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count); - bool is_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER; + eEvaluationMode eval_mode = DEG_get_mode(depsgraph); + bool is_render = eval_mode == DAG_EVAL_RENDER; - DEG_OBJECT_ITER_BEGIN (depsgraph, ob, flags) { - LineartObjectInfo *obi = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartObjectInfo)); - obi->usage = lineart_usage_check(scene->master_collection, ob, is_render); - obi->override_intersection_mask = lineart_intersection_mask_check(scene->master_collection, - ob); - Mesh *use_mesh; + FOREACH_SCENE_OBJECT_BEGIN (scene, ob) { + Object *eval_ob = DEG_get_evaluated_object(depsgraph, ob); - if (obi->usage == OBJECT_LRT_EXCLUDE) { + if (!eval_ob) { continue; } - Object *use_ob = DEG_get_evaluated_object(depsgraph, ob); - /* Prepare the matrix used for transforming this specific object (instance). This has to be - * done before mesh boundbox check because the function needs that. */ - mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, rb->view_projection, ob->obmat); - mul_m4db_m4db_m4fl_uniq(obi->model_view, rb->view, ob->obmat); - - if (!ELEM(use_ob->type, OB_MESH, OB_MBALL, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { - continue; - } - - if (!lineart_geometry_check_visible(obi->model_view_proj, rb->shift_x, rb->shift_y, use_ob)) { - if (G.debug_value == 4000) { - bound_box_discard_count++; - } - continue; - } - - if (use_ob->type == OB_MESH) { - use_mesh = BKE_object_get_evaluated_mesh(use_ob); + if (BKE_object_visibility(eval_ob, eval_mode) & OB_VISIBLE_SELF) { + lineart_object_load_single_instance( + rb, depsgraph, scene, eval_ob, eval_ob, eval_ob->obmat, is_render, olti, thread_count); } - else { - /* If DEG_ITER_OBJECT_FLAG_DUPLI is set, some curve objects may also have an evaluated mesh - * object in the list. To avoid adding duplicate geometry, ignore evaluated curve objects in - * those cases. */ - if (allow_duplicates && BKE_object_get_evaluated_mesh(ob) != NULL) { - continue; + if (allow_duplicates) { + ListBase *dupli = object_duplilist(depsgraph, scene, eval_ob); + LISTBASE_FOREACH (DupliObject *, dob, dupli) { + if (BKE_object_visibility(eval_ob, eval_mode) & + (OB_VISIBLE_PARTICLES | OB_VISIBLE_INSTANCES)) { + Object *ob_ref = (dob->type & OB_DUPLIPARTS) ? eval_ob : dob->ob; + lineart_object_load_single_instance( + rb, depsgraph, scene, dob->ob, ob_ref, dob->mat, is_render, olti, thread_count); + } } - use_mesh = BKE_mesh_new_from_object(depsgraph, use_ob, true, true); - } - - /* In case we still can not get any mesh geometry data from the object */ - if (!use_mesh) { - continue; - } - - if (ob->type != OB_MESH) { - obi->free_use_mesh = true; + free_object_duplilist(dupli); } - - /* Make normal matrix. */ - float imat[4][4]; - invert_m4_m4(imat, ob->obmat); - transpose_m4(imat); - copy_m4d_m4(obi->normal, imat); - - obi->original_me = use_mesh; - obi->original_ob = (ob->id.orig_id ? (Object *)ob->id.orig_id : (Object *)ob); - lineart_geometry_load_assign_thread(olti, obi, thread_count, use_mesh->totpoly); } - DEG_OBJECT_ITER_END; + FOREACH_SCENE_OBJECT_END; TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH); + if (G.debug_value == 4000) { + printf("thread count: %d\n", thread_count); + } for (int i = 0; i < thread_count; i++) { olti[i].rb = rb; - olti[i].dg = depsgraph; + olti[i].thread_id = i; BLI_task_pool_push(tp, (TaskRunFunction)lineart_object_load_worker, &olti[i], 0, NULL); } BLI_task_pool_work_and_wait(tp); @@ -2271,6 +2487,17 @@ static void lineart_main_load_geometries( * lineart_triangle_share_edge() can work properly from the lack of triangle adjacent info. */ int global_i = 0; + int edge_count = 0; + for (int i = 0; i < thread_count; i++) { + for (LineartObjectInfo *obi = olti[i].pending; obi; obi = obi->next) { + if (!obi->v_eln) { + continue; + } + edge_count += obi->pending_edges.next; + } + } + lineart_finalize_object_edge_array_reserve(&rb->pending_edges, edge_count); + for (int i = 0; i < thread_count; i++) { for (LineartObjectInfo *obi = olti[i].pending; obi; obi = obi->next) { if (!obi->v_eln) { @@ -2281,8 +2508,13 @@ static void lineart_main_load_geometries( for (int vi = 0; vi < v_count; vi++) { v[vi].index += global_i; } + /* Register a global index increment. See #lineart_triangle_share_edge() and + * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might + * eventually overflow, in such large scene it's virtually impossible for two vertex of the + * same numeric index to come close together. */ + obi->global_i_offset = global_i; global_i += v_count; - lineart_finalize_object_edge_list(rb, obi); + lineart_finalize_object_edge_array(&rb->pending_edges, obi); } } @@ -2987,7 +3219,7 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, result->flags = LRT_EDGE_FLAG_INTERSECTION; result->intersection_mask = (tri->intersection_mask | testing->intersection_mask); - lineart_prepend_edge_direct(&rb->intersection.first, result); + lineart_add_edge_to_array(&rb->pending_edges, result); return result; } @@ -3003,7 +3235,7 @@ static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb, double *G0 = tri->v[0]->gloc, *G1 = tri->v[1]->gloc, *G2 = tri->v[2]->gloc; - /* If this is not the smallest subdivision bounding area. */ + /* If this is not the smallest subdiv bounding area. */ if (ba->child) { lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[0]); lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[1]); @@ -3075,13 +3307,6 @@ static void lineart_destroy_render_data(LineartRenderBuffer *rb) return; } - memset(&rb->contour, 0, sizeof(ListBase)); - memset(&rb->crease, 0, sizeof(ListBase)); - memset(&rb->intersection, 0, sizeof(ListBase)); - memset(&rb->edge_mark, 0, sizeof(ListBase)); - memset(&rb->material, 0, sizeof(ListBase)); - memset(&rb->floating, 0, sizeof(ListBase)); - BLI_listbase_clear(&rb->chains); BLI_listbase_clear(&rb->wasted_cuts); @@ -3093,6 +3318,10 @@ static void lineart_destroy_render_data(LineartRenderBuffer *rb) BLI_spin_end(&rb->lock_cuts); BLI_spin_end(&rb->render_data_pool.lock_mem); + if (rb->pending_edges.array) { + MEM_freeN(rb->pending_edges.array); + } + lineart_mem_destroy(&rb->render_data_pool); } @@ -3187,7 +3416,6 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, rb->fuzzy_intersections = (lmd->calculation_flags & LRT_INTERSECTION_AS_CONTOUR) != 0; rb->fuzzy_everything = (lmd->calculation_flags & LRT_EVERYTHING_AS_CONTOUR) != 0; rb->allow_boundaries = (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) != 0; - rb->remove_doubles = (lmd->calculation_flags & LRT_REMOVE_DOUBLES) != 0; rb->use_loose_as_contour = (lmd->calculation_flags & LRT_LOOSE_AS_CONTOUR) != 0; rb->use_loose_edge_chain = (lmd->calculation_flags & LRT_CHAIN_LOOSE_EDGES) != 0; rb->use_geometry_space_chain = (lmd->calculation_flags & LRT_CHAIN_GEOMETRY_SPACE) != 0; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h index 508061d5d98..1a197c8b4b7 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h @@ -67,49 +67,11 @@ int lineart_count_intersection_segment_count(struct LineartRenderBuffer *rb); void lineart_count_and_print_render_buffer_memory(struct LineartRenderBuffer *rb); #define LRT_ITER_ALL_LINES_BEGIN \ - LineartEdge *e, *next_e; \ - void **current_head; \ - e = rb->contour.first; \ - if (!e) { \ - e = rb->crease.first; \ - } \ - if (!e) { \ - e = rb->material.first; \ - } \ - if (!e) { \ - e = rb->edge_mark.first; \ - } \ - if (!e) { \ - e = rb->intersection.first; \ - } \ - if (!e) { \ - e = rb->floating.first; \ - } \ - for (current_head = &rb->contour.first; e; e = next_e) { \ - next_e = e->next; - -#define LRT_ITER_ALL_LINES_NEXT \ - while (!next_e) { \ - if (current_head == &rb->contour.first) { \ - current_head = &rb->crease.first; \ - } \ - else if (current_head == &rb->crease.first) { \ - current_head = &rb->material.first; \ - } \ - else if (current_head == &rb->material.first) { \ - current_head = &rb->edge_mark.first; \ - } \ - else if (current_head == &rb->edge_mark.first) { \ - current_head = &rb->intersection.first; \ - } \ - else if (current_head == &rb->intersection.first) { \ - current_head = &rb->floating.first; \ - } \ - else { \ - break; \ - } \ - next_e = *current_head; \ - } + LineartEdge *e; \ + for (int i = 0; i < rb->pending_edges.next; i++) { \ + e = rb->pending_edges.array[i]; + +#define LRT_ITER_ALL_LINES_NEXT ; /* Doesn't do anything now with new array setup. */ #define LRT_ITER_ALL_LINES_END \ LRT_ITER_ALL_LINES_NEXT \ @@ -121,3 +83,13 @@ void lineart_count_and_print_render_buffer_memory(struct LineartRenderBuffer *rb /* Initial bounding area row/column count, setting 4 is the simplest way algorithm could function * efficiently. */ #define LRT_BA_ROWS 4 + +#ifdef __cplusplus +extern "C" { +#endif + +void lineart_sort_adjacent_items(LineartAdjacentEdge *ai, int length); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 7c6390971dd..f963d0e3b39 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -85,6 +85,7 @@ set(SRC GPU_buffers.h GPU_capabilities.h GPU_common.h + GPU_common_types.h GPU_compute.h GPU_context.h GPU_debug.h @@ -189,6 +190,7 @@ set(METAL_SRC metal/mtl_backend.mm metal/mtl_context.mm metal/mtl_debug.mm + metal/mtl_state.mm metal/mtl_texture.mm metal/mtl_texture_util.mm @@ -197,6 +199,7 @@ set(METAL_SRC metal/mtl_common.hh metal/mtl_context.hh metal/mtl_debug.hh + metal/mtl_state.hh metal/mtl_texture.hh ) @@ -301,7 +304,13 @@ set(GLSL_SRC shaders/gpu_shader_codegen_lib.glsl - shaders/gpu_shader_geometry.glsl + shaders/common/gpu_shader_common_color_ramp.glsl + shaders/common/gpu_shader_common_color_utils.glsl + shaders/common/gpu_shader_common_curves.glsl + shaders/common/gpu_shader_common_hash.glsl + shaders/common/gpu_shader_common_math.glsl + shaders/common/gpu_shader_common_math_utils.glsl + shaders/common/gpu_shader_common_mix_rgb.glsl shaders/material/gpu_shader_material_add_shader.glsl shaders/material/gpu_shader_material_ambient_occlusion.glsl @@ -315,8 +324,7 @@ set(GLSL_SRC shaders/material/gpu_shader_material_bump.glsl shaders/material/gpu_shader_material_camera.glsl shaders/material/gpu_shader_material_clamp.glsl - shaders/material/gpu_shader_material_color_ramp.glsl - shaders/material/gpu_shader_material_color_util.glsl + shaders/material/gpu_shader_material_combine_color.glsl shaders/material/gpu_shader_material_combine_hsv.glsl shaders/material/gpu_shader_material_combine_rgb.glsl shaders/material/gpu_shader_material_combine_xyz.glsl @@ -324,7 +332,6 @@ set(GLSL_SRC shaders/material/gpu_shader_material_displacement.glsl shaders/material/gpu_shader_material_eevee_specular.glsl shaders/material/gpu_shader_material_emission.glsl - shaders/material/gpu_shader_material_float_curve.glsl shaders/material/gpu_shader_material_fractal_noise.glsl shaders/material/gpu_shader_material_fresnel.glsl shaders/material/gpu_shader_material_gamma.glsl @@ -333,7 +340,6 @@ set(GLSL_SRC shaders/material/gpu_shader_material_glossy.glsl shaders/material/gpu_shader_material_hair_info.glsl shaders/material/gpu_shader_material_hair.glsl - shaders/material/gpu_shader_material_hash.glsl shaders/material/gpu_shader_material_holdout.glsl shaders/material/gpu_shader_material_hue_sat_val.glsl shaders/material/gpu_shader_material_invert.glsl @@ -342,9 +348,6 @@ set(GLSL_SRC shaders/material/gpu_shader_material_light_path.glsl shaders/material/gpu_shader_material_mapping.glsl shaders/material/gpu_shader_material_map_range.glsl - shaders/material/gpu_shader_material_math.glsl - shaders/material/gpu_shader_material_math_util.glsl - shaders/material/gpu_shader_material_mix_rgb.glsl shaders/material/gpu_shader_material_mix_shader.glsl shaders/material/gpu_shader_material_noise.glsl shaders/material/gpu_shader_material_normal.glsl @@ -357,8 +360,8 @@ set(GLSL_SRC shaders/material/gpu_shader_material_point_info.glsl shaders/material/gpu_shader_material_principled.glsl shaders/material/gpu_shader_material_refraction.glsl - shaders/material/gpu_shader_material_rgb_curves.glsl shaders/material/gpu_shader_material_rgb_to_bw.glsl + shaders/material/gpu_shader_material_separate_color.glsl shaders/material/gpu_shader_material_separate_hsv.glsl shaders/material/gpu_shader_material_separate_rgb.glsl shaders/material/gpu_shader_material_separate_xyz.glsl @@ -381,10 +384,10 @@ set(GLSL_SRC shaders/material/gpu_shader_material_tex_wave.glsl shaders/material/gpu_shader_material_tex_white_noise.glsl shaders/material/gpu_shader_material_toon.glsl + shaders/material/gpu_shader_material_transform_utils.glsl shaders/material/gpu_shader_material_translucent.glsl shaders/material/gpu_shader_material_transparent.glsl shaders/material/gpu_shader_material_uv_map.glsl - shaders/material/gpu_shader_material_vector_curves.glsl shaders/material/gpu_shader_material_vector_displacement.glsl shaders/material/gpu_shader_material_vector_math.glsl shaders/material/gpu_shader_material_vector_rotate.glsl @@ -496,6 +499,7 @@ set(SRC_SHADER_CREATE_INFOS shaders/infos/gpu_shader_2D_widget_info.hh shaders/infos/gpu_shader_3D_depth_only_info.hh shaders/infos/gpu_shader_3D_flat_color_info.hh + shaders/infos/gpu_shader_3D_image_info.hh shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh shaders/infos/gpu_shader_3D_point_info.hh shaders/infos/gpu_shader_3D_polyline_info.hh diff --git a/source/blender/gpu/GPU_common_types.h b/source/blender/gpu/GPU_common_types.h new file mode 100644 index 00000000000..e08143c2449 --- /dev/null +++ b/source/blender/gpu/GPU_common_types.h @@ -0,0 +1,18 @@ +/** \file + * \ingroup gpu + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum eGPUFrontFace { + GPU_CLOCKWISE, + GPU_COUNTERCLOCKWISE, +} eGPUFrontFace; + +#ifdef __cplusplus +} +#endif
\ No newline at end of file diff --git a/source/blender/gpu/GPU_legacy_stubs.h b/source/blender/gpu/GPU_legacy_stubs.h index 369347447f8..5970738a9b3 100644 --- a/source/blender/gpu/GPU_legacy_stubs.h +++ b/source/blender/gpu/GPU_legacy_stubs.h @@ -23,7 +23,7 @@ #include "BLI_utildefines.h" /** - * Empty function, use for breakpoint when a deprecated + * Empty function, use for break-point when a deprecated * OpenGL function is called. */ static void gl_deprecated(void) diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index b620fe9cc9d..3460d33fe68 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -280,6 +280,16 @@ typedef enum eGPUBuiltinShader { GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE, GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR, /** + * Draw a texture in 3D. Take a 3D position and a 2D texture coordinate for each vertex. + * + * Exposed via Python-API for add-ons. + * + * \param image: uniform sampler2D + * \param texCoord: in vec2 + * \param pos: in vec3 + */ + GPU_SHADER_3D_IMAGE, + /** * Draw texture with alpha. Take a 3D position and a 2D texture coordinate for each vertex. * * \param alpha: uniform float diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index 99b60351dcc..7519b3bc1cb 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -35,6 +35,19 @@ typedef enum eGPUBarrier { ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_ELEMENT_ARRAY) +/* NOTE: For Metal and Vulkan only. + * TODO(Metal): Update barrier calls to use stage flags. */ +typedef enum eGPUStageBarrierBits { + GPU_BARRIER_STAGE_VERTEX = (1 << 0), + GPU_BARRIER_STAGE_FRAGMENT = (1 << 1), + GPU_BARRIER_STAGE_COMPUTE = (1 << 2), + GPU_BARRIER_STAGE_ANY_GRAPHICS = (GPU_BARRIER_STAGE_VERTEX | GPU_BARRIER_STAGE_FRAGMENT), + GPU_BARRIER_STAGE_ANY = (GPU_BARRIER_STAGE_VERTEX | GPU_BARRIER_STAGE_FRAGMENT | + GPU_BARRIER_STAGE_COMPUTE), +} eGPUStageBarrierBits; + +ENUM_OPERATORS(eGPUStageBarrierBits, GPU_BARRIER_STAGE_COMPUTE) + /** * Defines the fixed pipeline blending equation. * SRC is the output color from the shader. diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index bb0912f284b..b045d908438 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -280,9 +280,9 @@ void *GPU_texture_read(GPUTexture *tex, eGPUDataFormat data_format, int miplvl); /** * Fills the whole texture with the same data for all pixels. * \warning Only work for 2D texture for now. - * \warning Only clears the mip 0 of the texture. + * \warning Only clears the MIP 0 of the texture. * \param data_format: data format of the pixel data. - * \note The format is float for unorm textures. + * \note The format is float for UNORM textures. * \param data: 1 pixel worth of data to fill the texture with. */ void GPU_texture_clear(GPUTexture *tex, eGPUDataFormat data_format, const void *data); diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h index fe6ed7eaf87..722ef878271 100644 --- a/source/blender/gpu/GPU_vertex_buffer.h +++ b/source/blender/gpu/GPU_vertex_buffer.h @@ -165,6 +165,7 @@ void GPU_vertbuf_tag_dirty(GPUVertBuf *verts); */ void GPU_vertbuf_use(GPUVertBuf *); void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding); +void GPU_vertbuf_bind_as_texture(struct GPUVertBuf *verts, int binding); void GPU_vertbuf_wrap_handle(GPUVertBuf *verts, uint64_t handle); diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h index c6b0d5902fe..bf8dc409cb7 100644 --- a/source/blender/gpu/GPU_vertex_format.h +++ b/source/blender/gpu/GPU_vertex_format.h @@ -55,7 +55,7 @@ typedef struct GPUVertAttr { /* 1 to 4 or 8 or 12 or 16 */ uint comp_len : 5; /* size in bytes, 1 to 64 */ - uint sz : 7; + uint size : 7; /* from beginning of vertex, in bytes */ uint offset : 11; /* up to GPU_VERT_ATTR_MAX_NAMES */ diff --git a/source/blender/gpu/intern/gpu_debug.cc b/source/blender/gpu/intern/gpu_debug.cc index c62a6416f92..055207eace8 100644 --- a/source/blender/gpu/intern/gpu_debug.cc +++ b/source/blender/gpu/intern/gpu_debug.cc @@ -50,11 +50,11 @@ void GPU_debug_get_groups_names(int name_buf_len, char *r_name_buf) r_name_buf[0] = '\0'; return; } - size_t sz = 0; + size_t len = 0; for (StringRef &name : stack) { - sz += BLI_snprintf_rlen(r_name_buf + sz, name_buf_len - sz, "%s > ", name.data()); + len += BLI_snprintf_rlen(r_name_buf + len, name_buf_len - len, "%s > ", name.data()); } - r_name_buf[sz - 3] = '\0'; + r_name_buf[len - 3] = '\0'; } bool GPU_debug_group_match(const char *ref) diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc index 7dc1c739750..69467e5b28a 100644 --- a/source/blender/gpu/intern/gpu_immediate.cc +++ b/source/blender/gpu/intern/gpu_immediate.cc @@ -490,7 +490,7 @@ static void immEndVertex() /* and move on to the next vertex */ #endif uchar *data = imm->vertex_data + a->offset; - memcpy(data, data - imm->vertex_format.stride, a->sz); + memcpy(data, data - imm->vertex_format.stride, a->size); /* TODO: consolidate copy of adjacent attributes */ } } diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 7ec6ee5183a..23028f58059 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -626,7 +626,7 @@ eGPUMaterialFlag GPU_material_flag(const GPUMaterial *mat) return mat->flag; } -/* Note: Consumes the flags. */ +/* NOTE: Consumes the flags. */ bool GPU_material_recalc_flag_get(GPUMaterial *mat) { bool updated = (mat->flag & GPU_MATFLAG_UPDATED) != 0; diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c index 1100272b712..b92fae4a89b 100644 --- a/source/blender/gpu/intern/gpu_shader_builtin.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -150,6 +150,11 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = { .name = "GPU_SHADER_SIMPLE_LIGHTING", .create_info = "gpu_shader_simple_lighting", }, + [GPU_SHADER_3D_IMAGE] = + { + .name = "GPU_SHADER_3D_IMAGE", + .create_info = "gpu_shader_3D_image", + }, [GPU_SHADER_3D_IMAGE_MODULATE_ALPHA] = { .name = "GPU_SHADER_3D_IMAGE_MODULATE_ALPHA", diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index f69c56b5f3f..aa2033b9154 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -593,7 +593,9 @@ struct GPUSource { bool is_from_material_library() const { - return filename.startswith("gpu_shader_material_") && filename.endswith(".glsl"); + return (filename.startswith("gpu_shader_material_") || + filename.startswith("gpu_shader_common_")) && + filename.endswith(".glsl"); } }; diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc index 2974547c858..f47970d48d1 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer.cc +++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc @@ -199,7 +199,7 @@ void GPU_vertbuf_attr_set(GPUVertBuf *verts_, uint a_idx, uint v_idx, const void BLI_assert(a_idx < format->attr_len); BLI_assert(verts->data != nullptr); verts->flag |= GPU_VERTBUF_DATA_DIRTY; - memcpy(verts->data + a->offset + v_idx * format->stride, data, a->sz); + memcpy(verts->data + a->offset + v_idx * format->stride, data, a->size); } void GPU_vertbuf_attr_fill(GPUVertBuf *verts_, uint a_idx, const void *data) @@ -208,7 +208,7 @@ void GPU_vertbuf_attr_fill(GPUVertBuf *verts_, uint a_idx, const void *data) const GPUVertFormat *format = &verts->format; BLI_assert(a_idx < format->attr_len); const GPUVertAttr *a = &format->attrs[a_idx]; - const uint stride = a->sz; /* tightly packed input data */ + const uint stride = a->size; /* tightly packed input data */ verts->flag |= GPU_VERTBUF_DATA_DIRTY; GPU_vertbuf_attr_fill_stride(verts_, a_idx, stride, data); } @@ -235,13 +235,13 @@ void GPU_vertbuf_attr_fill_stride(GPUVertBuf *verts_, uint a_idx, uint stride, c if (format->attr_len == 1 && stride == format->stride) { /* we can copy it all at once */ - memcpy(verts->data, data, vertex_len * a->sz); + memcpy(verts->data, data, vertex_len * a->size); } else { /* we must copy it per vertex */ for (uint v = 0; v < vertex_len; v++) { memcpy( - verts->data + a->offset + v * format->stride, (const uchar *)data + v * stride, a->sz); + verts->data + a->offset + v * format->stride, (const uchar *)data + v * stride, a->size); } } } @@ -256,7 +256,7 @@ void GPU_vertbuf_attr_get_raw_data(GPUVertBuf *verts_, uint a_idx, GPUVertBufRaw verts->flag |= GPU_VERTBUF_DATA_DIRTY; verts->flag &= ~GPU_VERTBUF_DATA_UPLOADED; - access->size = a->sz; + access->size = a->size; access->stride = format->stride; access->data = (uchar *)verts->data + a->offset; access->data_init = access->data; @@ -328,6 +328,11 @@ void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding) unwrap(verts)->bind_as_ssbo(binding); } +void GPU_vertbuf_bind_as_texture(struct GPUVertBuf *verts, int binding) +{ + unwrap(verts)->bind_as_texture(binding); +} + void GPU_vertbuf_update_sub(GPUVertBuf *verts, uint start, uint len, const void *data) { unwrap(verts)->update_sub(start, len, data); diff --git a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh index e5b70de9dfa..7a0b53cf958 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh @@ -51,6 +51,7 @@ class VertBuf { void resize(uint vert_len); void upload(); virtual void bind_as_ssbo(uint binding) = 0; + virtual void bind_as_texture(uint binding) = 0; virtual void wrap_handle(uint64_t handle) = 0; diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc index a9ac191754b..59ae862aa51 100644 --- a/source/blender/gpu/intern/gpu_vertex_format.cc +++ b/source/blender/gpu/intern/gpu_vertex_format.cc @@ -49,7 +49,7 @@ void GPU_vertformat_copy(GPUVertFormat *dest, const GPUVertFormat *src) memcpy(dest, src, sizeof(GPUVertFormat)); } -static uint comp_sz(GPUVertCompType type) +static uint comp_size(GPUVertCompType type) { #if TRUST_NO_ONE assert(type <= GPU_COMP_F32); /* other types have irregular sizes (not bytes) */ @@ -58,12 +58,12 @@ static uint comp_sz(GPUVertCompType type) return sizes[type]; } -static uint attr_sz(const GPUVertAttr *a) +static uint attr_size(const GPUVertAttr *a) { if (a->comp_type == GPU_COMP_I10) { return 4; /* always packed as 10_10_10_2 */ } - return a->comp_len * comp_sz(static_cast<GPUVertCompType>(a->comp_type)); + return a->comp_len * comp_size(static_cast<GPUVertCompType>(a->comp_type)); } static uint attr_align(const GPUVertAttr *a) @@ -71,7 +71,7 @@ static uint attr_align(const GPUVertAttr *a) if (a->comp_type == GPU_COMP_I10) { return 4; /* always packed as 10_10_10_2 */ } - uint c = comp_sz(static_cast<GPUVertCompType>(a->comp_type)); + uint c = comp_size(static_cast<GPUVertCompType>(a->comp_type)); if (a->comp_len == 3 && c <= 2) { return 4 * c; /* AMD HW can't fetch these well, so pad it out (other vendors too?) */ } @@ -156,7 +156,7 @@ uint GPU_vertformat_attr_add(GPUVertFormat *format, attr->comp_len = (comp_type == GPU_COMP_I10) ? 4 : comp_len; /* system needs 10_10_10_2 to be 4 or BGRA */ - attr->sz = attr_sz(attr); + attr->size = attr_size(attr); attr->offset = 0; /* offsets & stride are calculated later (during pack) */ attr->fetch_mode = fetch_mode; @@ -294,13 +294,13 @@ uint padding(uint offset, uint alignment) } #if PACK_DEBUG -static void show_pack(uint a_idx, uint sz, uint pad) +static void show_pack(uint a_idx, uint size, uint pad) { const char c = 'A' + a_idx; for (uint i = 0; i < pad; i++) { putchar('-'); } - for (uint i = 0; i < sz; i++) { + for (uint i = 0; i < size; i++) { putchar(c); } } @@ -310,10 +310,10 @@ void VertexFormat_pack(GPUVertFormat *format) { GPUVertAttr *a0 = &format->attrs[0]; a0->offset = 0; - uint offset = a0->sz; + uint offset = a0->size; #if PACK_DEBUG - show_pack(0, a0->sz, 0); + show_pack(0, a0->size, 0); #endif for (uint a_idx = 1; a_idx < format->attr_len; a_idx++) { @@ -321,10 +321,10 @@ void VertexFormat_pack(GPUVertFormat *format) uint mid_padding = padding(offset, attr_align(a)); offset += mid_padding; a->offset = offset; - offset += a->sz; + offset += a->size; #if PACK_DEBUG - show_pack(a_idx, a->sz, mid_padding); + show_pack(a_idx, a->size, mid_padding); #endif } diff --git a/source/blender/gpu/metal/mtl_context.hh b/source/blender/gpu/metal/mtl_context.hh index aa198482291..1849a04ea48 100644 --- a/source/blender/gpu/metal/mtl_context.hh +++ b/source/blender/gpu/metal/mtl_context.hh @@ -7,8 +7,10 @@ #include "gpu_context_private.hh" +#include "GPU_common_types.h" #include "GPU_context.h" +#include "mtl_capabilities.hh" #include "mtl_texture.hh" #include <Cocoa/Cocoa.h> @@ -21,6 +23,112 @@ namespace blender::gpu { +class MTLShader; +class MTLUniformBuf; +class MTLBuffer; + +/* Depth Stencil State */ +typedef struct MTLContextDepthStencilState { + + /* Depth State. */ + bool depth_write_enable; + bool depth_test_enabled; + float depth_range_near; + float depth_range_far; + MTLCompareFunction depth_function; + float depth_bias; + float depth_slope_scale; + bool depth_bias_enabled_for_points; + bool depth_bias_enabled_for_lines; + bool depth_bias_enabled_for_tris; + + /* Stencil State. */ + bool stencil_test_enabled; + unsigned int stencil_read_mask; + unsigned int stencil_write_mask; + unsigned int stencil_ref; + MTLCompareFunction stencil_func; + + MTLStencilOperation stencil_op_front_stencil_fail; + MTLStencilOperation stencil_op_front_depth_fail; + MTLStencilOperation stencil_op_front_depthstencil_pass; + + MTLStencilOperation stencil_op_back_stencil_fail; + MTLStencilOperation stencil_op_back_depth_fail; + MTLStencilOperation stencil_op_back_depthstencil_pass; + + /* Frame-buffer State -- We need to mark this, in case stencil state remains unchanged, + * but attachment state has changed. */ + bool has_depth_target; + bool has_stencil_target; + + /* TODO(Metal): Consider optimizing this function using memcmp. + * Un-used, but differing, stencil state leads to over-generation + * of state objects when doing trivial compare. */ + inline bool operator==(const MTLContextDepthStencilState &other) const + { + bool depth_state_equality = (has_depth_target == other.has_depth_target && + depth_write_enable == other.depth_write_enable && + depth_test_enabled == other.depth_test_enabled && + depth_function == other.depth_function); + + bool stencil_state_equality = true; + if (has_stencil_target) { + stencil_state_equality = + (has_stencil_target == other.has_stencil_target && + stencil_test_enabled == other.stencil_test_enabled && + stencil_op_front_stencil_fail == other.stencil_op_front_stencil_fail && + stencil_op_front_depth_fail == other.stencil_op_front_depth_fail && + stencil_op_front_depthstencil_pass == other.stencil_op_front_depthstencil_pass && + stencil_op_back_stencil_fail == other.stencil_op_back_stencil_fail && + stencil_op_back_depth_fail == other.stencil_op_back_depth_fail && + stencil_op_back_depthstencil_pass == other.stencil_op_back_depthstencil_pass && + stencil_func == other.stencil_func && stencil_read_mask == other.stencil_read_mask && + stencil_write_mask == other.stencil_write_mask); + } + + return depth_state_equality && stencil_state_equality; + } + + /* Depth stencil state will get hashed in order to prepare + * MTLDepthStencilState objects. The hash should comprise of + * all elements which fill the MTLDepthStencilDescriptor. + * These are bound when [rec setDepthStencilState:...] is called. + * Depth bias and stencil reference value are set dynamically on the RenderCommandEncoder: + * - setStencilReferenceValue: + * - setDepthBias:slopeScale:clamp: + */ + inline std::size_t hash() const + { + std::size_t boolean_bitmask = (this->depth_write_enable ? 1 : 0) | + ((this->depth_test_enabled ? 1 : 0) << 1) | + ((this->depth_bias_enabled_for_points ? 1 : 0) << 2) | + ((this->depth_bias_enabled_for_lines ? 1 : 0) << 3) | + ((this->depth_bias_enabled_for_tris ? 1 : 0) << 4) | + ((this->stencil_test_enabled ? 1 : 0) << 5) | + ((this->has_depth_target ? 1 : 0) << 6) | + ((this->has_stencil_target ? 1 : 0) << 7); + + std::size_t stencilop_bitmask = ((std::size_t)this->stencil_op_front_stencil_fail) | + ((std::size_t)this->stencil_op_front_depth_fail << 3) | + ((std::size_t)this->stencil_op_front_depthstencil_pass << 6) | + ((std::size_t)this->stencil_op_back_stencil_fail << 9) | + ((std::size_t)this->stencil_op_back_depth_fail << 12) | + ((std::size_t)this->stencil_op_back_depthstencil_pass << 15); + + std::size_t main_hash = (std::size_t)this->depth_function; + if (this->has_stencil_target) { + main_hash += (std::size_t)(this->stencil_read_mask & 0xFF) << 8; + main_hash += (std::size_t)(this->stencil_write_mask & 0xFF) << 16; + } + main_hash ^= (std::size_t)this->stencil_func << 16; + main_hash ^= stencilop_bitmask; + + std::size_t final_hash = (main_hash << 8) | boolean_bitmask; + return final_hash; + } +} MTLContextDepthStencilState; + typedef struct MTLContextTextureUtils { /* Depth Update Utilities */ @@ -108,11 +216,149 @@ typedef struct MTLContextTextureUtils { } MTLContextTextureUtils; +/* Structs containing information on current binding state for textures and samplers. */ +typedef struct MTLTextureBinding { + bool used; + + /* Same value as index in bindings array. */ + unsigned int texture_slot_index; + gpu::MTLTexture *texture_resource; + +} MTLTextureBinding; + +typedef struct MTLSamplerBinding { + bool used; + MTLSamplerState state; + + bool operator==(MTLSamplerBinding const &other) const + { + return (used == other.used && state == other.state); + } +} MTLSamplerBinding; + +/* Combined sampler state configuration for Argument Buffer caching. */ +struct MTLSamplerArray { + unsigned int num_samplers; + /* MTLSamplerState permutations between 0..256 - slightly more than a byte. */ + MTLSamplerState mtl_sampler_flags[MTL_MAX_TEXTURE_SLOTS]; + id<MTLSamplerState> mtl_sampler[MTL_MAX_TEXTURE_SLOTS]; + + inline bool operator==(const MTLSamplerArray &other) const + { + if (this->num_samplers != other.num_samplers) { + return false; + } + return (memcmp(this->mtl_sampler_flags, + other.mtl_sampler_flags, + sizeof(MTLSamplerState) * this->num_samplers) == 0); + } + + inline uint32_t hash() const + { + uint32_t hash = this->num_samplers; + for (int i = 0; i < this->num_samplers; i++) { + hash ^= (uint32_t)this->mtl_sampler_flags[i] << (i % 3); + } + return hash; + } +}; + +typedef enum MTLPipelineStateDirtyFlag { + MTL_PIPELINE_STATE_NULL_FLAG = 0, + /* Whether we need to call setViewport. */ + MTL_PIPELINE_STATE_VIEWPORT_FLAG = (1 << 0), + /* Whether we need to call setScissor.*/ + MTL_PIPELINE_STATE_SCISSOR_FLAG = (1 << 1), + /* Whether we need to update/rebind active depth stencil state. */ + MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG = (1 << 2), + /* Whether we need to update/rebind active PSO. */ + MTL_PIPELINE_STATE_PSO_FLAG = (1 << 3), + /* Whether we need to update the frontFacingWinding state. */ + MTL_PIPELINE_STATE_FRONT_FACING_FLAG = (1 << 4), + /* Whether we need to update the culling state. */ + MTL_PIPELINE_STATE_CULLMODE_FLAG = (1 << 5), + /* Full pipeline state needs applying. Occurs when beginning a new render pass. */ + MTL_PIPELINE_STATE_ALL_FLAG = + (MTL_PIPELINE_STATE_VIEWPORT_FLAG | MTL_PIPELINE_STATE_SCISSOR_FLAG | + MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG | MTL_PIPELINE_STATE_PSO_FLAG | + MTL_PIPELINE_STATE_FRONT_FACING_FLAG | MTL_PIPELINE_STATE_CULLMODE_FLAG) +} MTLPipelineStateDirtyFlag; + +/* Ignore full flag bit-mask `MTL_PIPELINE_STATE_ALL_FLAG`. */ +ENUM_OPERATORS(MTLPipelineStateDirtyFlag, MTL_PIPELINE_STATE_CULLMODE_FLAG); + +typedef struct MTLUniformBufferBinding { + bool bound; + MTLUniformBuf *ubo; +} MTLUniformBufferBinding; + typedef struct MTLContextGlobalShaderPipelineState { - /* ..TODO(Metal): More elements to be added as backend fleshed out.. */ + bool initialised; + + /* Whether the pipeline state has been modified since application. + * `dirty_flags` is a bitmask of the types of state which have been updated. + * This is in order to optimize calls and only re-apply state as needed. + * Some state parameters are dynamically applied on the RenderCommandEncoder, + * others may be encapsulated in GPU-resident state objects such as + * MTLDepthStencilState or MTLRenderPipelineState. */ + bool dirty; + MTLPipelineStateDirtyFlag dirty_flags; + + /* Shader resources. */ + MTLShader *null_shader; + + /* Active Shader State. */ + MTLShader *active_shader; + + /* Global Uniform Buffers. */ + MTLUniformBufferBinding ubo_bindings[MTL_MAX_UNIFORM_BUFFER_BINDINGS]; + + /* Context Texture bindings. */ + MTLTextureBinding texture_bindings[MTL_MAX_TEXTURE_SLOTS]; + MTLSamplerBinding sampler_bindings[MTL_MAX_SAMPLER_SLOTS]; + + /*** --- Render Pipeline State --- ***/ + /* Track global render pipeline state for the current context. The functions in GPU_state.h + * modify these parameters. Certain values, tagged [PSO], are parameters which are required to be + * passed into PSO creation, rather than dynamic state functions on the RenderCommandEncoder. + */ - /*** DATA and IMAGE access state ***/ + /* Blending State. */ + MTLColorWriteMask color_write_mask; /* [PSO] */ + bool blending_enabled; /* [PSO] */ + MTLBlendOperation alpha_blend_op; /* [PSO] */ + MTLBlendOperation rgb_blend_op; /* [PSO] */ + MTLBlendFactor dest_alpha_blend_factor; /* [PSO] */ + MTLBlendFactor dest_rgb_blend_factor; /* [PSO] */ + MTLBlendFactor src_alpha_blend_factor; /* [PSO] */ + MTLBlendFactor src_rgb_blend_factor; /* [PSO] */ + + /* Culling State. */ + bool culling_enabled; + eGPUFaceCullTest cull_mode; + eGPUFrontFace front_face; + + /* Depth State. */ + MTLContextDepthStencilState depth_stencil_state; + + /* Viewport/Scissor Region. */ + int viewport_offset_x; + int viewport_offset_y; + int viewport_width; + int viewport_height; + bool scissor_enabled; + int scissor_x; + int scissor_y; + int scissor_width; + int scissor_height; + + /* Image data access state. */ uint unpack_row_length; + + /* Render parameters. */ + float point_size = 1.0f; + float line_width = 1.0f; + } MTLContextGlobalShaderPipelineState; /* Metal Buffer */ @@ -127,8 +373,8 @@ typedef struct MTLTemporaryBufferRange { bool requires_flush(); } MTLTemporaryBufferRange; -/** MTLContext -- Core render loop and state management **/ -/* Note(Metal): Partial MTLContext stub to provide wrapper functionality +/** MTLContext -- Core render loop and state management. **/ +/* NOTE(Metal): Partial MTLContext stub to provide wrapper functionality * for work-in-progress MTL* classes. */ class MTLContext : public Context { @@ -138,8 +384,24 @@ class MTLContext : public Context { /* Compute and specialization caches. */ MTLContextTextureUtils texture_utils_; + /* Texture Samplers. */ + /* Cache of generated MTLSamplerState objects based on permutations of `eGPUSamplerState`. */ + id<MTLSamplerState> sampler_state_cache_[GPU_SAMPLER_MAX] = {0}; + id<MTLSamplerState> default_sampler_state_ = nil; + + /* When texture sampler count exceeds the resource bind limit, an + * argument buffer is used to pass samplers to the shader. + * Each unique configurations of multiple samplers can be cached, so as to not require + * re-generation. `samplers_` stores the current list of bound sampler objects. + * `cached_sampler_buffers_` is a cache of encoded argument buffers which can be re-used. */ + MTLSamplerArray samplers_; + blender::Map<MTLSamplerArray, gpu::MTLBuffer *> cached_sampler_buffers_; + public: - /* METAL API Resource Handles. */ + /* Shaders and Pipeline state. */ + MTLContextGlobalShaderPipelineState pipeline_state; + + /* Metal API Resource Handles. */ id<MTLCommandQueue> queue = nil; id<MTLDevice> device = nil; @@ -160,24 +422,40 @@ class MTLContext : public Context { void debug_group_begin(const char *name, int index) override; void debug_group_end(void) override; - /*** Context Utility functions */ + /*** MTLContext Utility functions. */ /* * All below functions modify the global state for the context, controlling the flow of * rendering, binding resources, setting global state, resource management etc; */ - /* Metal Context Core functions */ - /* Command Buffer Management */ + /* Metal Context Core functions. */ + /* Command Buffer Management. */ id<MTLCommandBuffer> get_active_command_buffer(); - /* Render Pass State and Management */ + /* Render Pass State and Management. */ void begin_render_pass(); void end_render_pass(); - - /* Shaders and Pipeline state */ - MTLContextGlobalShaderPipelineState pipeline_state; - - /* Texture utilities */ + bool is_render_pass_active(); + + /* Texture Binding. */ + void texture_bind(gpu::MTLTexture *mtl_texture, unsigned int texture_unit); + void sampler_bind(MTLSamplerState, unsigned int sampler_unit); + void texture_unbind(gpu::MTLTexture *mtl_texture); + void texture_unbind_all(void); + id<MTLSamplerState> get_sampler_from_state(MTLSamplerState state); + id<MTLSamplerState> generate_sampler_from_state(MTLSamplerState state); + id<MTLSamplerState> get_default_sampler_state(); + + /* Metal Context pipeline state. */ + void pipeline_state_init(void); + MTLShader *get_active_shader(void); + + /* State assignment. */ + void set_viewport(int origin_x, int origin_y, int width, int height); + void set_scissor(int scissor_x, int scissor_y, int scissor_width, int scissor_height); + void set_scissor_enabled(bool scissor_enabled); + + /* Texture utilities. */ MTLContextTextureUtils &get_texture_utils() { return this->texture_utils_; diff --git a/source/blender/gpu/metal/mtl_context.mm b/source/blender/gpu/metal/mtl_context.mm index 18ed38c373d..94f5682b11b 100644 --- a/source/blender/gpu/metal/mtl_context.mm +++ b/source/blender/gpu/metal/mtl_context.mm @@ -5,6 +5,11 @@ */ #include "mtl_context.hh" #include "mtl_debug.hh" +#include "mtl_state.hh" + +#include "DNA_userdef_types.h" + +#include "GPU_capabilities.h" using namespace blender; using namespace blender::gpu; @@ -44,6 +49,9 @@ MTLContext::MTLContext(void *ghost_window) /* Init debug. */ debug::mtl_debug_init(); + /* Initialize Metal modules. */ + this->state_manager = new MTLStateManager(this); + /* TODO(Metal): Implement. */ } @@ -98,6 +106,234 @@ void MTLContext::end_render_pass() /* TODO(Metal): Implement. */ } +bool MTLContext::is_render_pass_active() +{ + /* TODO(Metal): Implement. */ + return false; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Global Context State + * \{ */ + +/* Metal Context Pipeline State. */ +void MTLContext::pipeline_state_init() +{ + /*** Initialize state only once. ***/ + if (!this->pipeline_state.initialised) { + this->pipeline_state.initialised = true; + this->pipeline_state.active_shader = NULL; + + /* Clear bindings state. */ + for (int t = 0; t < GPU_max_textures(); t++) { + this->pipeline_state.texture_bindings[t].used = false; + this->pipeline_state.texture_bindings[t].texture_slot_index = t; + this->pipeline_state.texture_bindings[t].texture_resource = NULL; + } + for (int s = 0; s < MTL_MAX_SAMPLER_SLOTS; s++) { + this->pipeline_state.sampler_bindings[s].used = false; + } + for (int u = 0; u < MTL_MAX_UNIFORM_BUFFER_BINDINGS; u++) { + this->pipeline_state.ubo_bindings[u].bound = false; + this->pipeline_state.ubo_bindings[u].ubo = NULL; + } + } + + /*** State defaults -- restored by GPU_state_init. ***/ + /* Clear blending State. */ + this->pipeline_state.color_write_mask = MTLColorWriteMaskRed | MTLColorWriteMaskGreen | + MTLColorWriteMaskBlue | MTLColorWriteMaskAlpha; + this->pipeline_state.blending_enabled = false; + this->pipeline_state.alpha_blend_op = MTLBlendOperationAdd; + this->pipeline_state.rgb_blend_op = MTLBlendOperationAdd; + this->pipeline_state.dest_alpha_blend_factor = MTLBlendFactorZero; + this->pipeline_state.dest_rgb_blend_factor = MTLBlendFactorZero; + this->pipeline_state.src_alpha_blend_factor = MTLBlendFactorOne; + this->pipeline_state.src_rgb_blend_factor = MTLBlendFactorOne; + + /* Viewport and scissor. */ + this->pipeline_state.viewport_offset_x = 0; + this->pipeline_state.viewport_offset_y = 0; + this->pipeline_state.viewport_width = 0; + this->pipeline_state.viewport_height = 0; + this->pipeline_state.scissor_x = 0; + this->pipeline_state.scissor_y = 0; + this->pipeline_state.scissor_width = 0; + this->pipeline_state.scissor_height = 0; + this->pipeline_state.scissor_enabled = false; + + /* Culling State. */ + this->pipeline_state.culling_enabled = false; + this->pipeline_state.cull_mode = GPU_CULL_NONE; + this->pipeline_state.front_face = GPU_COUNTERCLOCKWISE; + + /* DATA and IMAGE access state. */ + this->pipeline_state.unpack_row_length = 0; + + /* Depth State. */ + this->pipeline_state.depth_stencil_state.depth_write_enable = false; + this->pipeline_state.depth_stencil_state.depth_test_enabled = false; + this->pipeline_state.depth_stencil_state.depth_range_near = 0.0; + this->pipeline_state.depth_stencil_state.depth_range_far = 1.0; + this->pipeline_state.depth_stencil_state.depth_function = MTLCompareFunctionAlways; + this->pipeline_state.depth_stencil_state.depth_bias = 0.0; + this->pipeline_state.depth_stencil_state.depth_slope_scale = 0.0; + this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_points = false; + this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_lines = false; + this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_tris = false; + + /* Stencil State. */ + this->pipeline_state.depth_stencil_state.stencil_test_enabled = false; + this->pipeline_state.depth_stencil_state.stencil_read_mask = 0xFF; + this->pipeline_state.depth_stencil_state.stencil_write_mask = 0xFF; + this->pipeline_state.depth_stencil_state.stencil_ref = 0; + this->pipeline_state.depth_stencil_state.stencil_func = MTLCompareFunctionAlways; + this->pipeline_state.depth_stencil_state.stencil_op_front_stencil_fail = MTLStencilOperationKeep; + this->pipeline_state.depth_stencil_state.stencil_op_front_depth_fail = MTLStencilOperationKeep; + this->pipeline_state.depth_stencil_state.stencil_op_front_depthstencil_pass = + MTLStencilOperationKeep; + this->pipeline_state.depth_stencil_state.stencil_op_back_stencil_fail = MTLStencilOperationKeep; + this->pipeline_state.depth_stencil_state.stencil_op_back_depth_fail = MTLStencilOperationKeep; + this->pipeline_state.depth_stencil_state.stencil_op_back_depthstencil_pass = + MTLStencilOperationKeep; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Texture State Management + * \{ */ + +void MTLContext::texture_bind(gpu::MTLTexture *mtl_texture, unsigned int texture_unit) +{ + BLI_assert(this); + BLI_assert(mtl_texture); + + if (texture_unit < 0 || texture_unit >= GPU_max_textures() || + texture_unit >= MTL_MAX_TEXTURE_SLOTS) { + MTL_LOG_WARNING("Attempting to bind texture '%s' to invalid texture unit %d\n", + mtl_texture->get_name(), + texture_unit); + BLI_assert(false); + return; + } + + /* Bind new texture. */ + this->pipeline_state.texture_bindings[texture_unit].texture_resource = mtl_texture; + this->pipeline_state.texture_bindings[texture_unit].used = true; + mtl_texture->is_bound_ = true; +} + +void MTLContext::sampler_bind(MTLSamplerState sampler_state, unsigned int sampler_unit) +{ + BLI_assert(this); + if (sampler_unit < 0 || sampler_unit >= GPU_max_textures() || + sampler_unit >= MTL_MAX_SAMPLER_SLOTS) { + MTL_LOG_WARNING("Attempting to bind sampler to invalid sampler unit %d\n", sampler_unit); + BLI_assert(false); + return; + } + + /* Apply binding. */ + this->pipeline_state.sampler_bindings[sampler_unit] = {true, sampler_state}; +} + +void MTLContext::texture_unbind(gpu::MTLTexture *mtl_texture) +{ + BLI_assert(mtl_texture); + + /* Iterate through textures in state and unbind. */ + for (int i = 0; i < min_uu(GPU_max_textures(), MTL_MAX_TEXTURE_SLOTS); i++) { + if (this->pipeline_state.texture_bindings[i].texture_resource == mtl_texture) { + this->pipeline_state.texture_bindings[i].texture_resource = nullptr; + this->pipeline_state.texture_bindings[i].used = false; + } + } + + /* Locally unbind texture. */ + mtl_texture->is_bound_ = false; +} + +void MTLContext::texture_unbind_all() +{ + /* Iterate through context's bound textures. */ + for (int t = 0; t < min_uu(GPU_max_textures(), MTL_MAX_TEXTURE_SLOTS); t++) { + if (this->pipeline_state.texture_bindings[t].used && + this->pipeline_state.texture_bindings[t].texture_resource) { + + this->pipeline_state.texture_bindings[t].used = false; + this->pipeline_state.texture_bindings[t].texture_resource = nullptr; + } + } +} + +id<MTLSamplerState> MTLContext::get_sampler_from_state(MTLSamplerState sampler_state) +{ + BLI_assert((unsigned int)sampler_state >= 0 && ((unsigned int)sampler_state) < GPU_SAMPLER_MAX); + return this->sampler_state_cache_[(unsigned int)sampler_state]; +} + +id<MTLSamplerState> MTLContext::generate_sampler_from_state(MTLSamplerState sampler_state) +{ + /* Check if sampler already exists for given state. */ + id<MTLSamplerState> st = this->sampler_state_cache_[(unsigned int)sampler_state]; + if (st != nil) { + return st; + } + else { + MTLSamplerDescriptor *descriptor = [[MTLSamplerDescriptor alloc] init]; + descriptor.normalizedCoordinates = true; + + MTLSamplerAddressMode clamp_type = (sampler_state.state & GPU_SAMPLER_CLAMP_BORDER) ? + MTLSamplerAddressModeClampToBorderColor : + MTLSamplerAddressModeClampToEdge; + descriptor.rAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_R) ? + MTLSamplerAddressModeRepeat : + clamp_type; + descriptor.sAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_S) ? + MTLSamplerAddressModeRepeat : + clamp_type; + descriptor.tAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_T) ? + MTLSamplerAddressModeRepeat : + clamp_type; + descriptor.borderColor = MTLSamplerBorderColorTransparentBlack; + descriptor.minFilter = (sampler_state.state & GPU_SAMPLER_FILTER) ? + MTLSamplerMinMagFilterLinear : + MTLSamplerMinMagFilterNearest; + descriptor.magFilter = (sampler_state.state & GPU_SAMPLER_FILTER) ? + MTLSamplerMinMagFilterLinear : + MTLSamplerMinMagFilterNearest; + descriptor.mipFilter = (sampler_state.state & GPU_SAMPLER_MIPMAP) ? + MTLSamplerMipFilterLinear : + MTLSamplerMipFilterNotMipmapped; + descriptor.lodMinClamp = -1000; + descriptor.lodMaxClamp = 1000; + float aniso_filter = max_ff(16, U.anisotropic_filter); + descriptor.maxAnisotropy = (sampler_state.state & GPU_SAMPLER_MIPMAP) ? aniso_filter : 1; + descriptor.compareFunction = (sampler_state.state & GPU_SAMPLER_COMPARE) ? + MTLCompareFunctionLessEqual : + MTLCompareFunctionAlways; + descriptor.supportArgumentBuffers = true; + + id<MTLSamplerState> state = [this->device newSamplerStateWithDescriptor:descriptor]; + this->sampler_state_cache_[(unsigned int)sampler_state] = state; + + BLI_assert(state != nil); + [descriptor autorelease]; + return state; + } +} + +id<MTLSamplerState> MTLContext::get_default_sampler_state() +{ + if (this->default_sampler_state_ == nil) { + this->default_sampler_state_ = this->get_sampler_from_state(DEFAULT_SAMPLER_STATE); + } + return this->default_sampler_state_; +} + /** \} */ } // blender::gpu diff --git a/source/blender/gpu/metal/mtl_state.hh b/source/blender/gpu/metal/mtl_state.hh new file mode 100644 index 00000000000..f2d85f9648b --- /dev/null +++ b/source/blender/gpu/metal/mtl_state.hh @@ -0,0 +1,73 @@ +/** \file + * \ingroup gpu + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" + +#include "GPU_state.h" +#include "gpu_state_private.hh" + +namespace blender::gpu { + +/* Forward Declarations. */ +class MTLContext; + +/** + * State manager keeping track of the draw state and applying it before drawing. + * Metal Implementation. + **/ +class MTLStateManager : public StateManager { + public: + private: + /* Current state of the associated MTLContext. + * Avoids resetting the whole state for every change. */ + GPUState current_; + GPUStateMutable current_mutable_; + MTLContext *context_; + + public: + MTLStateManager(MTLContext *ctx); + + void apply_state(void) override; + void force_state(void) override; + + void issue_barrier(eGPUBarrier barrier_bits) override; + + void texture_bind(Texture *tex, eGPUSamplerState sampler, int unit) override; + void texture_unbind(Texture *tex) override; + void texture_unbind_all(void) override; + + void image_bind(Texture *tex, int unit) override; + void image_unbind(Texture *tex) override; + void image_unbind_all(void) override; + + void texture_unpack_row_length_set(uint len) override; + + private: + void set_write_mask(const eGPUWriteMask value); + void set_depth_test(const eGPUDepthTest value); + void set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation); + void set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state); + void set_clip_distances(const int new_dist_len, const int old_dist_len); + void set_logic_op(const bool enable); + void set_facing(const bool invert); + void set_backface_culling(const eGPUFaceCullTest test); + void set_provoking_vert(const eGPUProvokingVertex vert); + void set_shadow_bias(const bool enable); + void set_blend(const eGPUBlend value); + + void set_state(const GPUState &state); + void set_mutable_state(const GPUStateMutable &state); + + /* METAL State utility functions. */ + void mtl_state_init(void); + void mtl_depth_range(float near, float far); + void mtl_stencil_mask(unsigned int mask); + void mtl_stencil_set_func(eGPUStencilTest stencil_func, int ref, unsigned int mask); + + MEM_CXX_CLASS_ALLOC_FUNCS("MTLStateManager") +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/metal/mtl_state.mm b/source/blender/gpu/metal/mtl_state.mm new file mode 100644 index 00000000000..5f52bc55f72 --- /dev/null +++ b/source/blender/gpu/metal/mtl_state.mm @@ -0,0 +1,675 @@ +/** \file + * \ingroup gpu + */ + +#include "BLI_math_base.h" +#include "BLI_math_bits.h" + +#include "GPU_framebuffer.h" + +#include "mtl_context.hh" +#include "mtl_state.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name MTLStateManager + * \{ */ + +void MTLStateManager::mtl_state_init(void) +{ + BLI_assert(this->context_); + this->context_->pipeline_state_init(); +} + +MTLStateManager::MTLStateManager(MTLContext *ctx) : StateManager() +{ + /* Initialize State. */ + this->context_ = ctx; + mtl_state_init(); + + /* Force update using default state. */ + current_ = ~state; + current_mutable_ = ~mutable_state; + set_state(state); + set_mutable_state(mutable_state); +} + +void MTLStateManager::apply_state(void) +{ + this->set_state(this->state); + this->set_mutable_state(this->mutable_state); + /* TODO(Metal): Enable after integration of MTLFrameBuffer. */ + /* static_cast<MTLFrameBuffer *>(this->context_->active_fb)->apply_state(); */ +}; + +void MTLStateManager::force_state(void) +{ + /* Little exception for clip distances since they need to keep the old count correct. */ + uint32_t clip_distances = current_.clip_distances; + current_ = ~this->state; + current_.clip_distances = clip_distances; + current_mutable_ = ~this->mutable_state; + this->set_state(this->state); + this->set_mutable_state(this->mutable_state); +}; + +void MTLStateManager::set_state(const GPUState &state) +{ + GPUState changed = state ^ current_; + + if (changed.blend != 0) { + set_blend((eGPUBlend)state.blend); + } + if (changed.write_mask != 0) { + set_write_mask((eGPUWriteMask)state.write_mask); + } + if (changed.depth_test != 0) { + set_depth_test((eGPUDepthTest)state.depth_test); + } + if (changed.stencil_test != 0 || changed.stencil_op != 0) { + 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((eGPUFaceCullTest)state.culling_test); + } + if (changed.logic_op_xor != 0) { + set_logic_op(state.logic_op_xor); + } + if (changed.invert_facing != 0) { + set_facing(state.invert_facing); + } + if (changed.provoking_vert != 0) { + set_provoking_vert((eGPUProvokingVertex)state.provoking_vert); + } + if (changed.shadow_bias != 0) { + set_shadow_bias(state.shadow_bias); + } + + /* TODO remove (Following GLState). */ + if (changed.polygon_smooth) { + /* NOTE: Unsupported in Metal. */ + } + if (changed.line_smooth) { + /* NOTE: Unsupported in Metal. */ + } + + current_ = state; +} + +void MTLStateManager::mtl_depth_range(float near, float far) +{ + BLI_assert(this->context_); + BLI_assert(near >= 0.0 && near < 1.0); + BLI_assert(far > 0.0 && far <= 1.0); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state; + + ds_state.depth_range_near = near; + ds_state.depth_range_far = far; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_VIEWPORT_FLAG; +} + +void MTLStateManager::set_mutable_state(const GPUStateMutable &state) +{ + GPUStateMutable changed = state ^ current_mutable_; + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + + if (float_as_uint(changed.point_size) != 0) { + pipeline_state.point_size = state.point_size; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG; + } + + if (changed.line_width != 0) { + pipeline_state.line_width = state.line_width; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG; + } + + if (changed.depth_range[0] != 0 || changed.depth_range[1] != 0) { + /* TODO remove, should modify the projection matrix instead. */ + mtl_depth_range(state.depth_range[0], state.depth_range[1]); + } + + if (changed.stencil_compare_mask != 0 || changed.stencil_reference != 0 || + changed.stencil_write_mask != 0) { + set_stencil_mask((eGPUStencilTest)current_.stencil_test, state); + } + + current_mutable_ = state; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name State setting functions + * \{ */ + +void MTLStateManager::set_write_mask(const eGPUWriteMask value) +{ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + pipeline_state.depth_stencil_state.depth_write_enable = ((value & GPU_WRITE_DEPTH) != 0); + pipeline_state.color_write_mask = + (((value & GPU_WRITE_RED) != 0) ? MTLColorWriteMaskRed : MTLColorWriteMaskNone) | + (((value & GPU_WRITE_GREEN) != 0) ? MTLColorWriteMaskGreen : MTLColorWriteMaskNone) | + (((value & GPU_WRITE_BLUE) != 0) ? MTLColorWriteMaskBlue : MTLColorWriteMaskNone) | + (((value & GPU_WRITE_ALPHA) != 0) ? MTLColorWriteMaskAlpha : MTLColorWriteMaskNone); + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG; +} + +static MTLCompareFunction gpu_depth_function_to_metal(eGPUDepthTest depth_func) +{ + switch (depth_func) { + case GPU_DEPTH_NONE: + return MTLCompareFunctionNever; + case GPU_DEPTH_LESS: + return MTLCompareFunctionLess; + case GPU_DEPTH_EQUAL: + return MTLCompareFunctionEqual; + case GPU_DEPTH_LESS_EQUAL: + return MTLCompareFunctionLessEqual; + case GPU_DEPTH_GREATER: + return MTLCompareFunctionGreater; + case GPU_DEPTH_GREATER_EQUAL: + return MTLCompareFunctionGreaterEqual; + case GPU_DEPTH_ALWAYS: + return MTLCompareFunctionAlways; + default: + BLI_assert(false && "Invalid eGPUDepthTest"); + break; + } + return MTLCompareFunctionAlways; +} + +static MTLCompareFunction gpu_stencil_func_to_metal(eGPUStencilTest stencil_func) +{ + switch (stencil_func) { + case GPU_STENCIL_NONE: + return MTLCompareFunctionAlways; + case GPU_STENCIL_EQUAL: + return MTLCompareFunctionEqual; + case GPU_STENCIL_NEQUAL: + return MTLCompareFunctionNotEqual; + case GPU_STENCIL_ALWAYS: + return MTLCompareFunctionAlways; + default: + BLI_assert(false && "Unrecognised eGPUStencilTest function"); + break; + } + return MTLCompareFunctionAlways; +} + +void MTLStateManager::set_depth_test(const eGPUDepthTest value) +{ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state; + + ds_state.depth_test_enabled = (value != GPU_DEPTH_NONE); + ds_state.depth_function = gpu_depth_function_to_metal(value); + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; +} + +void MTLStateManager::mtl_stencil_mask(unsigned int mask) +{ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + pipeline_state.depth_stencil_state.stencil_write_mask = mask; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; +} + +void MTLStateManager::mtl_stencil_set_func(eGPUStencilTest stencil_func, + int ref, + unsigned int mask) +{ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state; + + ds_state.stencil_func = gpu_stencil_func_to_metal(stencil_func); + ds_state.stencil_ref = ref; + ds_state.stencil_read_mask = mask; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; +} + +static void mtl_stencil_set_op_separate(MTLContext *context, + eGPUFaceCullTest face, + MTLStencilOperation stencil_fail, + MTLStencilOperation depth_test_fail, + MTLStencilOperation depthstencil_pass) +{ + BLI_assert(context); + MTLContextGlobalShaderPipelineState &pipeline_state = context->pipeline_state; + MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state; + + if (face == GPU_CULL_FRONT) { + ds_state.stencil_op_front_stencil_fail = stencil_fail; + ds_state.stencil_op_front_depth_fail = depth_test_fail; + ds_state.stencil_op_front_depthstencil_pass = depthstencil_pass; + } + else if (face == GPU_CULL_BACK) { + ds_state.stencil_op_back_stencil_fail = stencil_fail; + ds_state.stencil_op_back_depth_fail = depth_test_fail; + ds_state.stencil_op_back_depthstencil_pass = depthstencil_pass; + } + + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; +} + +static void mtl_stencil_set_op(MTLContext *context, + MTLStencilOperation stencil_fail, + MTLStencilOperation depth_test_fail, + MTLStencilOperation depthstencil_pass) +{ + mtl_stencil_set_op_separate( + context, GPU_CULL_FRONT, stencil_fail, depth_test_fail, depthstencil_pass); + mtl_stencil_set_op_separate( + context, GPU_CULL_BACK, stencil_fail, depth_test_fail, depthstencil_pass); +} + +void MTLStateManager::set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation) +{ + switch (operation) { + case GPU_STENCIL_OP_REPLACE: + mtl_stencil_set_op(this->context_, + MTLStencilOperationKeep, + MTLStencilOperationKeep, + MTLStencilOperationReplace); + break; + case GPU_STENCIL_OP_COUNT_DEPTH_PASS: + /* Winding inversed due to flipped Y coordinate system in Metal. */ + mtl_stencil_set_op_separate(this->context_, + GPU_CULL_FRONT, + MTLStencilOperationKeep, + MTLStencilOperationKeep, + MTLStencilOperationIncrementWrap); + mtl_stencil_set_op_separate(this->context_, + GPU_CULL_BACK, + MTLStencilOperationKeep, + MTLStencilOperationKeep, + MTLStencilOperationDecrementWrap); + break; + case GPU_STENCIL_OP_COUNT_DEPTH_FAIL: + /* Winding inversed due to flipped Y coordinate system in Metal. */ + mtl_stencil_set_op_separate(this->context_, + GPU_CULL_FRONT, + MTLStencilOperationKeep, + MTLStencilOperationDecrementWrap, + MTLStencilOperationKeep); + mtl_stencil_set_op_separate(this->context_, + GPU_CULL_BACK, + MTLStencilOperationKeep, + MTLStencilOperationIncrementWrap, + MTLStencilOperationKeep); + break; + case GPU_STENCIL_OP_NONE: + default: + mtl_stencil_set_op(this->context_, + MTLStencilOperationKeep, + MTLStencilOperationKeep, + MTLStencilOperationKeep); + } + + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + pipeline_state.depth_stencil_state.stencil_test_enabled = (test != GPU_STENCIL_NONE); + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; +} + +void MTLStateManager::set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state) +{ + if (test == GPU_STENCIL_NONE) { + mtl_stencil_mask(0x00); + mtl_stencil_set_func(GPU_STENCIL_ALWAYS, 0x00, 0x00); + } + else { + mtl_stencil_mask(state.stencil_write_mask); + mtl_stencil_set_func(test, state.stencil_reference, state.stencil_compare_mask); + } +} + +void MTLStateManager::set_clip_distances(const int new_dist_len, const int old_dist_len) +{ + /* TODO(Metal): Support Clip distances in METAL. Clip distance + * assignment via shader is supported, but global clip-states require + * support. */ +} + +void MTLStateManager::set_logic_op(const bool enable) +{ + /* NOTE(Metal): Logic Operations not directly supported. */ +} + +void MTLStateManager::set_facing(const bool invert) +{ + /* Check Current Context. */ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + + /* Apply State -- opposite of GL, as METAL default is GPU_CLOCKWISE, GL default is + * COUNTERCLOCKWISE. This needs to be the inverse of the default. */ + pipeline_state.front_face = (invert) ? GPU_COUNTERCLOCKWISE : GPU_CLOCKWISE; + + /* Mark Dirty - Ensure context updates state between draws. */ + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_FRONT_FACING_FLAG; + pipeline_state.dirty = true; +} + +void MTLStateManager::set_backface_culling(const eGPUFaceCullTest test) +{ + /* Check Current Context. */ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + + /* Apply State. */ + pipeline_state.culling_enabled = (test != GPU_CULL_NONE); + pipeline_state.cull_mode = test; + + /* Mark Dirty - Ensure context updates state between draws. */ + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_CULLMODE_FLAG; + pipeline_state.dirty = true; +} + +void MTLStateManager::set_provoking_vert(const eGPUProvokingVertex vert) +{ + /* NOTE(Metal): Provoking vertex is not a feature in the Metal API. + * Shaders are handled on a case-by-case basis using a modified vertex shader. + * For example, wireframe rendering and edit-mesh shaders utilize an SSBO-based + * vertex fetching mechanism which considers the inverse convention for flat + * shading, to ensure consistent results with OpenGL. */ +} + +void MTLStateManager::set_shadow_bias(const bool enable) +{ + /* Check Current Context. */ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state; + + /* Apply State. */ + if (enable) { + ds_state.depth_bias_enabled_for_lines = true; + ds_state.depth_bias_enabled_for_tris = true; + ds_state.depth_bias = 2.0f; + ds_state.depth_slope_scale = 1.0f; + } + else { + ds_state.depth_bias_enabled_for_lines = false; + ds_state.depth_bias_enabled_for_tris = false; + ds_state.depth_bias = 0.0f; + ds_state.depth_slope_scale = 0.0f; + } + + /* Mark Dirty - Ensure context updates depth-stencil state between draws. */ + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; + pipeline_state.dirty = true; +} + +void MTLStateManager::set_blend(const eGPUBlend value) +{ + /** + * Factors to the equation. + * SRC is fragment shader output. + * DST is framebuffer color. + * final.rgb = SRC.rgb * src_rgb + DST.rgb * dst_rgb; + * final.a = SRC.a * src_alpha + DST.a * dst_alpha; + **/ + MTLBlendFactor src_rgb; + MTLBlendFactor dst_rgb; + MTLBlendFactor src_alpha; + MTLBlendFactor dst_alpha; + switch (value) { + default: + case GPU_BLEND_ALPHA: { + src_rgb = MTLBlendFactorSourceAlpha; + dst_rgb = MTLBlendFactorOneMinusSourceAlpha; + src_alpha = MTLBlendFactorOne; + dst_alpha = MTLBlendFactorOneMinusSourceAlpha; + break; + } + case GPU_BLEND_ALPHA_PREMULT: { + src_rgb = MTLBlendFactorOne; + dst_rgb = MTLBlendFactorOneMinusSourceAlpha; + src_alpha = MTLBlendFactorOne; + dst_alpha = MTLBlendFactorOneMinusSourceAlpha; + break; + } + case GPU_BLEND_ADDITIVE: { + /* Do not let alpha accumulate but pre-multiply the source RGB by it. */ + src_rgb = MTLBlendFactorSourceAlpha; + dst_rgb = MTLBlendFactorOne; + src_alpha = MTLBlendFactorZero; + dst_alpha = MTLBlendFactorOne; + break; + } + case GPU_BLEND_SUBTRACT: + case GPU_BLEND_ADDITIVE_PREMULT: { + /* Let alpha accumulate. */ + src_rgb = MTLBlendFactorOne; + dst_rgb = MTLBlendFactorOne; + src_alpha = MTLBlendFactorOne; + dst_alpha = MTLBlendFactorOne; + break; + } + case GPU_BLEND_MULTIPLY: { + src_rgb = MTLBlendFactorDestinationColor; + dst_rgb = MTLBlendFactorZero; + src_alpha = MTLBlendFactorDestinationAlpha; + dst_alpha = MTLBlendFactorZero; + break; + } + case GPU_BLEND_INVERT: { + src_rgb = MTLBlendFactorOneMinusDestinationColor; + dst_rgb = MTLBlendFactorZero; + src_alpha = MTLBlendFactorZero; + dst_alpha = MTLBlendFactorOne; + break; + } + case GPU_BLEND_OIT: { + src_rgb = MTLBlendFactorOne; + dst_rgb = MTLBlendFactorOne; + src_alpha = MTLBlendFactorZero; + dst_alpha = MTLBlendFactorOneMinusSourceAlpha; + break; + } + case GPU_BLEND_BACKGROUND: { + src_rgb = MTLBlendFactorOneMinusDestinationAlpha; + dst_rgb = MTLBlendFactorSourceAlpha; + src_alpha = MTLBlendFactorZero; + dst_alpha = MTLBlendFactorSourceAlpha; + break; + } + case GPU_BLEND_ALPHA_UNDER_PREMUL: { + src_rgb = MTLBlendFactorOneMinusDestinationAlpha; + dst_rgb = MTLBlendFactorOne; + src_alpha = MTLBlendFactorOneMinusDestinationAlpha; + dst_alpha = MTLBlendFactorOne; + break; + } + case GPU_BLEND_CUSTOM: { + src_rgb = MTLBlendFactorOne; + dst_rgb = MTLBlendFactorSource1Color; + src_alpha = MTLBlendFactorOne; + dst_alpha = MTLBlendFactorSource1Alpha; + break; + } + } + + /* Check Current Context. */ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + + if (value == GPU_BLEND_SUBTRACT) { + pipeline_state.rgb_blend_op = MTLBlendOperationReverseSubtract; + pipeline_state.alpha_blend_op = MTLBlendOperationReverseSubtract; + } + else { + pipeline_state.rgb_blend_op = MTLBlendOperationAdd; + pipeline_state.alpha_blend_op = MTLBlendOperationAdd; + } + + /* Apply State. */ + pipeline_state.blending_enabled = (value != GPU_BLEND_NONE); + pipeline_state.src_rgb_blend_factor = src_rgb; + pipeline_state.dest_rgb_blend_factor = dst_rgb; + pipeline_state.src_alpha_blend_factor = src_alpha; + pipeline_state.dest_alpha_blend_factor = dst_alpha; + + /* Mark Dirty - Ensure context updates PSOs between draws. */ + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG; + pipeline_state.dirty = true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Memory barrier + * \{ */ + +/* NOTE(Metal): Granular option for specifying before/after stages for a barrier + * Would be a useful feature. */ +/*void MTLStateManager::issue_barrier(eGPUBarrier barrier_bits, + eGPUStageBarrierBits before_stages, + eGPUStageBarrierBits after_stages) */ +void MTLStateManager::issue_barrier(eGPUBarrier barrier_bits) +{ + /* NOTE(Metal): The Metal API implicitly tracks dependencies between resources. + * Memory barriers and execution barriers (Fences/Events) can be used to coordinate + * this explicitly, however, in most cases, the driver will be able to + * resolve these dependencies automatically. + * For untracked resources, such as MTLHeap's, explicit barriers are necessary. */ + eGPUStageBarrierBits before_stages = GPU_BARRIER_STAGE_ANY; + eGPUStageBarrierBits after_stages = GPU_BARRIER_STAGE_ANY; + + MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get()); + BLI_assert(ctx); + if (ctx->is_render_pass_active()) { + + /* Apple Silicon does not support memory barriers. + * We do not currently need these due to implicit API guarantees. + * NOTE(Metal): MTLFence/MTLEvent may be required to synchronize work if + * untracked resources are ever used. */ + if ([ctx->device hasUnifiedMemory]) { + return; + } + + /* Issue barrier. */ + /* TODO(Metal): To be completed pending implementation of RenderCommandEncoder management. */ + id<MTLRenderCommandEncoder> rec = nil; // ctx->get_active_render_command_encoder(); + BLI_assert(rec); + + /* Only supporting Metal on 10.15 onward anyway - Check required for warnings. */ + if (@available(macOS 10.14, *)) { + MTLBarrierScope scope = 0; + if (barrier_bits & GPU_BARRIER_SHADER_IMAGE_ACCESS || + barrier_bits & GPU_BARRIER_TEXTURE_FETCH) { + scope = scope | MTLBarrierScopeTextures | MTLBarrierScopeRenderTargets; + } + if (barrier_bits & GPU_BARRIER_SHADER_STORAGE || + barrier_bits & GPU_BARRIER_VERTEX_ATTRIB_ARRAY || + barrier_bits & GPU_BARRIER_ELEMENT_ARRAY) { + scope = scope | MTLBarrierScopeBuffers; + } + + MTLRenderStages before_stage_flags = 0; + MTLRenderStages after_stage_flags = 0; + if (before_stages & GPU_BARRIER_STAGE_VERTEX && + !(before_stages & GPU_BARRIER_STAGE_FRAGMENT)) { + before_stage_flags = before_stage_flags | MTLRenderStageVertex; + } + if (before_stages & GPU_BARRIER_STAGE_FRAGMENT) { + before_stage_flags = before_stage_flags | MTLRenderStageFragment; + } + if (after_stages & GPU_BARRIER_STAGE_VERTEX) { + after_stage_flags = after_stage_flags | MTLRenderStageVertex; + } + if (after_stages & GPU_BARRIER_STAGE_FRAGMENT) { + after_stage_flags = MTLRenderStageFragment; + } + + if (scope != 0) { + [rec memoryBarrierWithScope:scope + afterStages:after_stage_flags + beforeStages:before_stage_flags]; + } + } + } +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Texture State Management + * \{ */ + +void MTLStateManager::texture_unpack_row_length_set(uint len) +{ + /* Set source image row data stride when uploading image data to the GPU. */ + MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get())); + ctx->pipeline_state.unpack_row_length = len; +} + +void MTLStateManager::texture_bind(Texture *tex_, eGPUSamplerState sampler_type, int unit) +{ + BLI_assert(tex_); + gpu::MTLTexture *mtl_tex = static_cast<gpu::MTLTexture *>(tex_); + BLI_assert(mtl_tex); + + MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get())); + if (unit >= 0) { + ctx->texture_bind(mtl_tex, unit); + + /* Fetching textures default sampler configuration and applying + * eGPUSampler State on top. This path exists to support + * Any of the sampler state which is associated with the + * texture itself such as min/max mip levels. */ + MTLSamplerState sampler = mtl_tex->get_sampler_state(); + sampler.state = sampler_type; + + ctx->sampler_bind(sampler, unit); + } +} + +void MTLStateManager::texture_unbind(Texture *tex_) +{ + BLI_assert(tex_); + gpu::MTLTexture *mtl_tex = static_cast<gpu::MTLTexture *>(tex_); + BLI_assert(mtl_tex); + MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get())); + ctx->texture_unbind(mtl_tex); +} + +void MTLStateManager::texture_unbind_all(void) +{ + MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get())); + BLI_assert(ctx); + ctx->texture_unbind_all(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Image Binding (from image load store) + * \{ */ + +void MTLStateManager::image_bind(Texture *tex_, int unit) +{ + this->texture_bind(tex_, GPU_SAMPLER_DEFAULT, unit); +} + +void MTLStateManager::image_unbind(Texture *tex_) +{ + this->texture_unbind(tex_); +} + +void MTLStateManager::image_unbind_all(void) +{ + this->texture_unbind_all(); +} + +/** \} */ + +} // blender::gpu diff --git a/source/blender/gpu/metal/mtl_texture.hh b/source/blender/gpu/metal/mtl_texture.hh index e013bb5321a..b820256ec36 100644 --- a/source/blender/gpu/metal/mtl_texture.hh +++ b/source/blender/gpu/metal/mtl_texture.hh @@ -47,16 +47,13 @@ struct TextureUpdateRoutineSpecialisation { (component_count_input == other.component_count_input) && (component_count_output == other.component_count_output)); } -}; -template<> struct blender::DefaultHash<TextureUpdateRoutineSpecialisation> { - inline uint64_t operator()(const TextureUpdateRoutineSpecialisation &key) const + inline uint64_t hash() const { - - DefaultHash<std::string> string_hasher; + blender::DefaultHash<std::string> string_hasher; return (uint64_t)string_hasher( - key.input_data_type + key.output_data_type + - std::to_string((key.component_count_input << 8) + key.component_count_output)); + this->input_data_type + this->output_data_type + + std::to_string((this->component_count_input << 8) + this->component_count_output)); } }; @@ -78,12 +75,10 @@ struct DepthTextureUpdateRoutineSpecialisation { { return ((data_mode == other.data_mode)); } -}; -template<> struct blender::DefaultHash<DepthTextureUpdateRoutineSpecialisation> { - inline uint64_t operator()(const DepthTextureUpdateRoutineSpecialisation &key) const + inline uint64_t hash() const { - return (uint64_t)(key.data_mode); + return (uint64_t)(this->data_mode); } }; @@ -109,17 +104,14 @@ struct TextureReadRoutineSpecialisation { (component_count_output == other.component_count_output) && (depth_format_mode == other.depth_format_mode)); } -}; -template<> struct blender::DefaultHash<TextureReadRoutineSpecialisation> { - inline uint64_t operator()(const TextureReadRoutineSpecialisation &key) const + inline uint64_t hash() const { - - DefaultHash<std::string> string_hasher; - return (uint64_t)string_hasher(key.input_data_type + key.output_data_type + - std::to_string((key.component_count_input << 8) + - key.component_count_output + - (key.depth_format_mode << 28))); + blender::DefaultHash<std::string> string_hasher; + return (uint64_t)string_hasher(this->input_data_type + this->output_data_type + + std::to_string((this->component_count_input << 8) + + this->component_count_output + + (this->depth_format_mode << 28))); } }; @@ -158,21 +150,6 @@ typedef struct MTLSamplerState { const MTLSamplerState DEFAULT_SAMPLER_STATE = {GPU_SAMPLER_DEFAULT /*, 0, 9999*/}; -} // namespace blender::gpu - -template<> struct blender::DefaultHash<blender::gpu::MTLSamplerState> { - inline uint64_t operator()(const blender::gpu::MTLSamplerState &key) const - { - const DefaultHash<unsigned int> uint_hasher; - uint64_t main_hash = (uint64_t)uint_hasher((unsigned int)(key.state)); - - /* Hash other parameters as needed. */ - return main_hash; - } -}; - -namespace blender::gpu { - class MTLTexture : public Texture { friend class MTLContext; friend class MTLStateManager; @@ -242,7 +219,7 @@ class MTLTexture : public Texture { id<MTLBuffer> vert_buffer_mtl_; int vert_buffer_offset_; - /* Core parameters and subresources. */ + /* Core parameters and sub-resources. */ eGPUTextureUsage gpu_image_usage_flags_; /* Whether the texture's properties or state has changed (e.g. mipmap range), and re-baking of diff --git a/source/blender/gpu/metal/mtl_texture.mm b/source/blender/gpu/metal/mtl_texture.mm index df3efdd12e7..ca19d1f9e4b 100644 --- a/source/blender/gpu/metal/mtl_texture.mm +++ b/source/blender/gpu/metal/mtl_texture.mm @@ -563,7 +563,7 @@ void gpu::MTLTexture::update_sub( return; } - /* Check Format writeability. */ + /* Check Format write-ability. */ if (mtl_format_get_writeable_view_format(destination_format) == MTLPixelFormatInvalid) { MTL_LOG_ERROR( "[Error]: Updating texture -- destination MTLPixelFormat '%d' does not support write " @@ -1163,7 +1163,7 @@ void gpu::MTLTexture::mip_range_set(int min, int max) { BLI_assert(min <= max && min >= 0 && max <= mipmaps_); - /* Note: + /* NOTE: * - mip_min_ and mip_max_ are used to Clamp LODs during sampling. * - Given functions like Framebuffer::recursive_downsample modifies the mip range * between each layer, we do not want to be re-baking the texture. @@ -1769,8 +1769,8 @@ void gpu::MTLTexture::ensure_baked() /* CUBE TEXTURES */ case GPU_TEXTURE_CUBE: case GPU_TEXTURE_CUBE_ARRAY: { - /* Note: For a cubemap 'Texture::d_' refers to total number of faces, not just array slices - */ + /* NOTE: For a cube-map 'Texture::d_' refers to total number of faces, + * not just array slices. */ BLI_assert(this->w_ > 0 && this->h_ > 0); this->texture_descriptor_ = [[MTLTextureDescriptor alloc] init]; this->texture_descriptor_.pixelFormat = mtl_format; diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh index 2dde8d6c86b..e5b879f1f15 100644 --- a/source/blender/gpu/opengl/gl_texture.hh +++ b/source/blender/gpu/opengl/gl_texture.hh @@ -363,7 +363,7 @@ inline GLenum to_gl_data_format(eGPUTextureFormat format) } /** - * Assume Unorm / Float target. Used with #glReadPixels. + * Assume UNORM/Float target. Used with #glReadPixels. */ inline GLenum channel_len_to_gl(int channel_len) { diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc index 04f60f10d41..cfcf77fe705 100644 --- a/source/blender/gpu/opengl/gl_vertex_array.cc +++ b/source/blender/gpu/opengl/gl_vertex_array.cc @@ -39,8 +39,8 @@ static uint16_t vbo_bind(const ShaderInterface *interface, const GPUVertAttr *a = &format->attrs[a_idx]; if (format->deinterleaved) { - offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].sz) * v_len; - stride = a->sz; + offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].size) * v_len; + stride = a->size; } else { offset = a->offset; diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.cc b/source/blender/gpu/opengl/gl_vertex_buffer.cc index a7a0c92431f..6942a220892 100644 --- a/source/blender/gpu/opengl/gl_vertex_buffer.cc +++ b/source/blender/gpu/opengl/gl_vertex_buffer.cc @@ -5,6 +5,8 @@ * \ingroup gpu */ +#include "GPU_texture.h" + #include "gl_context.hh" #include "gl_vertex_buffer.hh" @@ -38,6 +40,7 @@ void GLVertBuf::release_data() } if (vbo_id_ != 0) { + GPU_TEXTURE_FREE_SAFE(buffer_texture_); GLContext::buf_free(vbo_id_); vbo_id_ = 0; memory_usage -= vbo_size_; @@ -51,6 +54,7 @@ void GLVertBuf::duplicate_data(VertBuf *dst_) BLI_assert(GLContext::get() != nullptr); GLVertBuf *src = this; GLVertBuf *dst = static_cast<GLVertBuf *>(dst_); + dst->buffer_texture_ = nullptr; if (src->vbo_id_ != 0) { dst->vbo_size_ = src->size_used_get(); @@ -111,6 +115,16 @@ void GLVertBuf::bind_as_ssbo(uint binding) glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, vbo_id_); } +void GLVertBuf::bind_as_texture(uint binding) +{ + bind(); + BLI_assert(vbo_id_ != 0); + if (buffer_texture_ == nullptr) { + buffer_texture_ = GPU_texture_create_from_vertbuf("vertbuf_as_texture", wrap(this)); + } + GPU_texture_bind(buffer_texture_, binding); +} + const void *GLVertBuf::read() const { BLI_assert(is_active()); diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.hh b/source/blender/gpu/opengl/gl_vertex_buffer.hh index 4c29c17dcf7..88d2a455778 100644 --- a/source/blender/gpu/opengl/gl_vertex_buffer.hh +++ b/source/blender/gpu/opengl/gl_vertex_buffer.hh @@ -11,6 +11,8 @@ #include "glew-mx.h" +#include "GPU_texture.h" + #include "gpu_vertex_buffer_private.hh" namespace blender { @@ -23,6 +25,8 @@ class GLVertBuf : public VertBuf { private: /** OpenGL buffer handle. Init on first upload. Immutable after that. */ GLuint vbo_id_ = 0; + /** Texture used if the buffer is bound as buffer texture. Init on first use. */ + struct ::GPUTexture *buffer_texture_ = nullptr; /** Defines whether the buffer handle is wrapped by this GLVertBuf, i.e. we do not own it and * should not free it. */ bool is_wrapper_ = false; @@ -46,6 +50,7 @@ class GLVertBuf : public VertBuf { void upload_data() override; void duplicate_data(VertBuf *dst) override; void bind_as_ssbo(uint binding) override; + void bind_as_texture(uint binding) override; private: bool is_active() const; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_color_ramp.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_color_ramp.glsl index 17240da4050..17240da4050 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_color_ramp.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_color_ramp.glsl diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_color_util.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl index a5c3a990d90..fe89985ae7f 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_color_util.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl @@ -90,6 +90,56 @@ void hsv_to_rgb(vec4 hsv, out vec4 outcol) outcol = vec4(rgb, hsv.w); } +void rgb_to_hsl(vec4 rgb, out vec4 outcol) +{ + float cmax, cmin, h, s, l; + + cmax = max(rgb[0], max(rgb[1], rgb[2])); + cmin = min(rgb[0], min(rgb[1], rgb[2])); + l = min(1.0, (cmax + cmin) / 2.0); + + if (cmax == cmin) { + h = s = 0.0; /* achromatic */ + } + else { + float cdelta = cmax - cmin; + s = l > 0.5 ? cdelta / (2.0 - cmax - cmin) : cdelta / (cmax + cmin); + if (cmax == rgb[0]) { + h = (rgb[1] - rgb[2]) / cdelta + (rgb[1] < rgb[2] ? 6.0 : 0.0); + } + else if (cmax == rgb[1]) { + h = (rgb[2] - rgb[0]) / cdelta + 2.0; + } + else { + h = (rgb[0] - rgb[1]) / cdelta + 4.0; + } + } + h /= 6.0; + + outcol = vec4(h, s, l, rgb.w); +} + +void hsl_to_rgb(vec4 hsl, out vec4 outcol) +{ + float nr, ng, nb, chroma, h, s, l; + + h = hsl[0]; + s = hsl[1]; + l = hsl[2]; + + nr = abs(h * 6.0 - 3.0) - 1.0; + ng = 2.0 - abs(h * 6.0 - 2.0); + nb = 2.0 - abs(h * 6.0 - 4.0); + + nr = clamp(nr, 0.0, 1.0); + nb = clamp(nb, 0.0, 1.0); + ng = clamp(ng, 0.0, 1.0); + + chroma = (1.0 - abs(2.0 * l - 1.0)) * s; + + outcol = vec4((nr - 0.5) * chroma + l, (ng - 0.5) * chroma + l, (nb - 0.5) * chroma + l, hsl.w); +} + void color_alpha_clear(vec4 color, out vec4 result) { result = vec4(color.rgb, 1.0); diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl new file mode 100644 index 00000000000..8948ed77557 --- /dev/null +++ b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl @@ -0,0 +1,162 @@ +vec4 white_balance(vec4 color, vec4 black_level, vec4 white_level) +{ + vec4 range = max(white_level - black_level, vec4(1e-5f)); + return (color - black_level) / range; +} + +float extrapolate_if_needed(float parameter, float value, float start_slope, float end_slope) +{ + if (parameter < 0.0) { + return value + parameter * start_slope; + } + + if (parameter > 1.0) { + return value + (parameter - 1.0) * end_slope; + } + + return value; +} + +/* Same as extrapolate_if_needed but vectorized. */ +vec3 extrapolate_if_needed(vec3 parameters, vec3 values, vec3 start_slopes, vec3 end_slopes) +{ + vec3 end_or_zero_slopes = mix(vec3(0.0), end_slopes, greaterThan(parameters, vec3(1.0))); + vec3 slopes = mix(end_or_zero_slopes, start_slopes, lessThan(parameters, vec3(0.0))); + parameters = parameters - mix(vec3(0.0), vec3(1.0), greaterThan(parameters, vec3(1.0))); + return values + parameters * slopes; +} + +/* Curve maps are stored in sampler objects that are evaluated in the [0, 1] range, so normalize + * parameters accordingly. */ +#define NORMALIZE_PARAMETER(parameter, minimum, range) ((parameter - minimum) * range) + +void curves_combined_rgb(float factor, + vec4 color, + vec4 black_level, + vec4 white_level, + sampler1DArray curve_map, + const float layer, + vec4 range_minimums, + vec4 range_dividers, + vec4 start_slopes, + vec4 end_slopes, + out vec4 result) +{ + vec4 balanced = white_balance(color, black_level, white_level); + + /* First, evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the + * UI. */ + vec3 parameters = NORMALIZE_PARAMETER(balanced.rgb, range_minimums.aaa, range_dividers.aaa); + result.r = texture(curve_map, vec2(parameters.x, layer)).a; + result.g = texture(curve_map, vec2(parameters.y, layer)).a; + result.b = texture(curve_map, vec2(parameters.z, layer)).a; + + /* Then, extrapolate if needed. */ + result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.aaa, end_slopes.aaa); + + /* Then, evaluate each channel on its curve map. */ + parameters = NORMALIZE_PARAMETER(result.rgb, range_minimums.rgb, range_dividers.rgb); + result.r = texture(curve_map, vec2(parameters.r, layer)).r; + result.g = texture(curve_map, vec2(parameters.g, layer)).g; + result.b = texture(curve_map, vec2(parameters.b, layer)).b; + + /* Then, extrapolate again if needed. */ + result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.rgb, end_slopes.rgb); + result.a = color.a; + + result = mix(color, result, factor); +} + +void curves_combined_only(float factor, + vec4 color, + vec4 black_level, + vec4 white_level, + sampler1DArray curve_map, + const float layer, + float range_minimum, + float range_divider, + float start_slope, + float end_slope, + out vec4 result) +{ + vec4 balanced = white_balance(color, black_level, white_level); + + /* Evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the + * UI. */ + vec3 parameters = NORMALIZE_PARAMETER(balanced.rgb, range_minimum, range_divider); + result.r = texture(curve_map, vec2(parameters.x, layer)).a; + result.g = texture(curve_map, vec2(parameters.y, layer)).a; + result.b = texture(curve_map, vec2(parameters.z, layer)).a; + + /* Then, extrapolate if needed. */ + result.rgb = extrapolate_if_needed(parameters, result.rgb, vec3(start_slope), vec3(end_slope)); + result.a = color.a; + + result = mix(color, result, factor); +} + +void curves_vector(vec3 vector, + sampler1DArray curve_map, + const float layer, + vec3 range_minimums, + vec3 range_dividers, + vec3 start_slopes, + vec3 end_slopes, + out vec3 result) +{ + /* Evaluate each component on its curve map. */ + vec3 parameters = NORMALIZE_PARAMETER(vector, range_minimums, range_dividers); + result.x = texture(curve_map, vec2(parameters.x, layer)).x; + result.y = texture(curve_map, vec2(parameters.y, layer)).y; + result.z = texture(curve_map, vec2(parameters.z, layer)).z; + + /* Then, extrapolate if needed. */ + result = extrapolate_if_needed(parameters, result, start_slopes, end_slopes); +} + +void curves_vector_mixed(float factor, + vec3 vector, + sampler1DArray curve_map, + const float layer, + vec3 range_minimums, + vec3 range_dividers, + vec3 start_slopes, + vec3 end_slopes, + out vec3 result) +{ + curves_vector( + vector, curve_map, layer, range_minimums, range_dividers, start_slopes, end_slopes, result); + result = mix(vector, result, factor); +} + +void curves_float(float value, + sampler1DArray curve_map, + const float layer, + float range_minimum, + float range_divider, + float start_slope, + float end_slope, + out float result) +{ + /* Evaluate the normalized value on the first curve map. */ + float parameter = NORMALIZE_PARAMETER(value, range_minimum, range_divider); + result = texture(curve_map, vec2(parameter, layer)).x; + + /* Then, extrapolate if needed. */ + result = extrapolate_if_needed(parameter, result, start_slope, end_slope); +} + +void curves_float_mixed(float factor, + float value, + sampler1DArray curve_map, + const float layer, + float range_minimum, + float range_divider, + float start_slope, + float end_slope, + out float result) +{ + curves_float( + value, curve_map, layer, range_minimum, range_divider, start_slope, end_slope, result); + result = mix(value, result, factor); +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_hash.glsl index 32d61f06a65..32d61f06a65 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_hash.glsl diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_math.glsl index 0948ce2c9fa..5f640f64056 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_math.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) void math_add(float a, float b, float c, out float result) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl index 91a8996939a..124654963fd 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl @@ -139,75 +139,3 @@ mat3 euler_to_mat3(vec3 euler) mat[2][2] = cy * cx; return mat; } - -void normal_transform_object_to_world(vec3 vin, out vec3 vout) -{ - vout = normal_object_to_world(vin); -} - -void normal_transform_world_to_object(vec3 vin, out vec3 vout) -{ - vout = normal_world_to_object(vin); -} - -void direction_transform_object_to_world(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ModelMatrix, vin); -} - -void direction_transform_object_to_view(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ModelMatrix, vin); - vout = transform_direction(ViewMatrix, vout); -} - -void direction_transform_view_to_world(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ViewMatrixInverse, vin); -} - -void direction_transform_view_to_object(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ViewMatrixInverse, vin); - vout = transform_direction(ModelMatrixInverse, vout); -} - -void direction_transform_world_to_view(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ViewMatrix, vin); -} - -void direction_transform_world_to_object(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ModelMatrixInverse, vin); -} - -void point_transform_object_to_world(vec3 vin, out vec3 vout) -{ - vout = point_object_to_world(vin); -} - -void point_transform_object_to_view(vec3 vin, out vec3 vout) -{ - vout = point_object_to_view(vin); -} - -void point_transform_view_to_world(vec3 vin, out vec3 vout) -{ - vout = point_view_to_world(vin); -} - -void point_transform_view_to_object(vec3 vin, out vec3 vout) -{ - vout = point_view_to_object(vin); -} - -void point_transform_world_to_view(vec3 vin, out vec3 vout) -{ - vout = point_world_to_view(vin); -} - -void point_transform_world_to_object(vec3 vin, out vec3 vout) -{ - vout = point_world_to_object(vin); -} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl index 157a6a27c15..f9652f1150b 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) void mix_blend(float fac, vec4 col1, vec4 col2, out vec4 outcol) { diff --git a/source/blender/gpu/shaders/gpu_shader_geometry.glsl b/source/blender/gpu/shaders/gpu_shader_geometry.glsl deleted file mode 100644 index e0e899cfb35..00000000000 --- a/source/blender/gpu/shaders/gpu_shader_geometry.glsl +++ /dev/null @@ -1,93 +0,0 @@ - -#define INTERP_FACE_VARYING_2(result, fvarOffset, tessCoord) \ - { \ - vec2 v[4]; \ - int primOffset = (gl_PrimitiveID + PrimitiveIdBase) * 4; \ - for (int i = 0; i < 4; i++) { \ - int index = (primOffset + i) * osd_fvar_count + fvarOffset; \ - v[i] = vec2(texelFetch(FVarDataBuffer, index).s, texelFetch(FVarDataBuffer, index + 1).s); \ - } \ - result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); \ - } - -#define INTERP_FACE_VARYING_ATT_2(result, fvarOffset, tessCoord) \ - { \ - vec2 tmp; \ - INTERP_FACE_VARYING_2(tmp, fvarOffset, tessCoord); \ - result = vec3(tmp, 0); \ - } - -out block -{ - VertexData v; -} -outpt; - -void set_mtface_vertex_attrs(vec2 st); - -void emit_flat(int index, vec3 normal) -{ - outpt.v.position = inpt[index].v.position; - outpt.v.normal = normal; - - /* Compatibility */ - varnormal = outpt.v.normal; - varposition = outpt.v.position.xyz; - - /* TODO(sergey): Only uniform subdivisions atm. */ - vec2 quadst[4] = vec2[](vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1)); - vec2 st = quadst[index]; - - INTERP_FACE_VARYING_2(outpt.v.uv, osd_active_uv_offset, st); - - set_mtface_vertex_attrs(st); - - gl_Position = ProjectionMatrix * inpt[index].v.position; - EmitVertex(); -} - -void emit_smooth(int index) -{ - outpt.v.position = inpt[index].v.position; - outpt.v.normal = inpt[index].v.normal; - - /* Compatibility */ - varnormal = outpt.v.normal; - varposition = outpt.v.position.xyz; - - /* TODO(sergey): Only uniform subdivisions atm. */ - vec2 quadst[4] = vec2[](vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1)); - vec2 st = quadst[index]; - - INTERP_FACE_VARYING_2(outpt.v.uv, osd_active_uv_offset, st); - - set_mtface_vertex_attrs(st); - - gl_Position = ProjectionMatrix * inpt[index].v.position; - EmitVertex(); -} - -void main() -{ - gl_PrimitiveID = gl_PrimitiveIDIn; - - if (osd_flat_shading) { - vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz; - vec3 B = (inpt[3].v.position - inpt[1].v.position).xyz; - vec3 flat_normal = normalize(cross(B, A)); - emit_flat(0, flat_normal); - emit_flat(1, flat_normal); - emit_flat(3, flat_normal); - emit_flat(2, flat_normal); - } - else { - emit_smooth(0); - emit_smooth(1); - emit_smooth(3); - emit_smooth(2); - } - EndPrimitive(); -} - -void set_mtface_vertex_attrs(vec2 st) -{ diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh new file mode 100644 index 00000000000..94cf58933af --- /dev/null +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup gpu + */ + +#include "gpu_interface_info.hh" +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(gpu_shader_3D_image) + .vertex_in(0, Type::VEC3, "pos") + .vertex_in(1, Type::VEC2, "texCoord") + .vertex_out(smooth_tex_coord_interp_iface) + .fragment_out(0, Type::VEC4, "fragColor") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .sampler(0, ImageType::FLOAT_2D, "image") + .vertex_source("gpu_shader_3D_image_vert.glsl") + .fragment_source("gpu_shader_image_frag.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh index 369fe3ac293..ae7edeb16e2 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh @@ -28,7 +28,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_clipped_uniform_color) .fragment_out(0, Type::VEC4, "fragColor") .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .push_constant(Type::VEC4, "color") - /* TODO(fclem): Put thoses two to one UBO. */ + /* TODO(@fclem): Put those two to one UBO. */ .push_constant(Type::MAT4, "ModelMatrix") .push_constant(Type::VEC4, "ClipPlane") .vertex_source("gpu_shader_3D_clipped_uniform_color_vert.glsl") diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_combine_color.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_combine_color.glsl new file mode 100644 index 00000000000..e68d0d98484 --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_combine_color.glsl @@ -0,0 +1,16 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void combine_color_rgb(float r, float g, float b, out vec4 col) +{ + col = vec4(r, g, b, 1.0); +} + +void combine_color_hsv(float h, float s, float v, out vec4 col) +{ + hsv_to_rgb(vec4(h, s, v, 1.0), col); +} + +void combine_color_hsl(float h, float s, float l, out vec4 col) +{ + hsl_to_rgb(vec4(h, s, l, 1.0), col); +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl index e8f444080b9..4d9e16afe66 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) void combine_hsv(float h, float s, float v, out vec4 col) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl deleted file mode 100644 index 514409f7fdf..00000000000 --- a/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl +++ /dev/null @@ -1,33 +0,0 @@ -/* ext is vec4(in_x, in_dy, out_x, out_dy). */ -float curve_float_extrapolate(float x, float y, vec4 ext) -{ - if (x < 0.0) { - return y + x * ext.y; - } - else if (x > 1.0) { - return y + (x - 1.0) * ext.w; - } - else { - return y; - } -} - -#define RANGE_RESCALE(x, min, range) ((x - min) * range) - -void curve_float(float fac, - float vec, - sampler1DArray curvemap, - float layer, - float range, - vec4 ext, - out float outvec) -{ - float xyz_min = ext.x; - vec = RANGE_RESCALE(vec, xyz_min, range); - - outvec = texture(curvemap, vec2(vec, layer)).x; - - outvec = curve_float_extrapolate(vec, outvec, ext); - - outvec = mix(vec, outvec, fac); -} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl index 7f502f74c0c..6d52e97cca1 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl) /* The fractal_noise functions are all exactly the same except for the input type. */ diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl index 29fb09ceebd..64681cd795a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) void node_gamma(vec4 col, float gamma, out vec4 outcol) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl index 2d5114c3bad..61458b05c86 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) void node_hair_info(float hair_length, out float is_strand, diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl index 30b808508e9..5223828e176 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) void hue_sat(float hue, float sat, float value, float fac, vec4 col, out vec4 outcol) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl index 119ee3c0eaa..a81e6d36a55 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) float smootherstep(float edge0, float edge1, float x) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl index 312c57231c5..b59257506c9 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) void mapping_mat4( vec3 vec, vec4 m0, vec4 m1, vec4 m2, vec4 m3, vec3 minvec, vec3 maxvec, out vec3 outvec) diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl index c84f34a834c..881e38ea11a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) /* clang-format off */ #define FLOORFRAC(x, x_int, x_fract) { float x_floor = floor(x); x_int = int(x_floor); x_fract = x - x_floor; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl index ad3d4737193..251103ae57c 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) void node_point_info(out vec3 position, out float radius, out float random) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_rgb_curves.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_rgb_curves.glsl deleted file mode 100644 index 054fdddf7c3..00000000000 --- a/source/blender/gpu/shaders/material/gpu_shader_material_rgb_curves.glsl +++ /dev/null @@ -1,73 +0,0 @@ -/* ext is vec4(in_x, in_dy, out_x, out_dy). */ -float curve_extrapolate(float x, float y, vec4 ext) -{ - if (x < 0.0) { - return y + x * ext.y; - } - else if (x > 1.0) { - return y + (x - 1.0) * ext.w; - } - else { - return y; - } -} - -#define RANGE_RESCALE(x, min, range) ((x - min) * range) - -void curves_rgb(float fac, - vec4 col, - sampler1DArray curvemap, - float layer, - vec4 range, - vec4 ext_r, - vec4 ext_g, - vec4 ext_b, - vec4 ext_a, - out vec4 outcol) -{ - vec4 co = vec4(RANGE_RESCALE(col.rgb, ext_a.x, range.a), layer); - vec3 samp; - samp.r = texture(curvemap, co.xw).a; - samp.g = texture(curvemap, co.yw).a; - samp.b = texture(curvemap, co.zw).a; - - samp.r = curve_extrapolate(co.x, samp.r, ext_a); - samp.g = curve_extrapolate(co.y, samp.g, ext_a); - samp.b = curve_extrapolate(co.z, samp.b, ext_a); - - vec3 rgb_min = vec3(ext_r.x, ext_g.x, ext_b.x); - co.xyz = RANGE_RESCALE(samp.rgb, rgb_min, range.rgb); - - samp.r = texture(curvemap, co.xw).r; - samp.g = texture(curvemap, co.yw).g; - samp.b = texture(curvemap, co.zw).b; - - outcol.r = curve_extrapolate(co.x, samp.r, ext_r); - outcol.g = curve_extrapolate(co.y, samp.g, ext_g); - outcol.b = curve_extrapolate(co.z, samp.b, ext_b); - outcol.a = col.a; - - outcol = mix(col, outcol, fac); -} - -void curves_rgb_opti(float fac, - vec4 col, - sampler1DArray curvemap, - float layer, - vec4 range, - vec4 ext_a, - out vec4 outcol) -{ - vec4 co = vec4(RANGE_RESCALE(col.rgb, ext_a.x, range.a), layer); - vec3 samp; - samp.r = texture(curvemap, co.xw).a; - samp.g = texture(curvemap, co.yw).a; - samp.b = texture(curvemap, co.zw).a; - - outcol.r = curve_extrapolate(co.x, samp.r, ext_a); - outcol.g = curve_extrapolate(co.y, samp.g, ext_a); - outcol.b = curve_extrapolate(co.z, samp.b, ext_a); - outcol.a = col.a; - - outcol = mix(col, outcol, fac); -} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_separate_color.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_separate_color.glsl new file mode 100644 index 00000000000..2dd51029cef --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_separate_color.glsl @@ -0,0 +1,28 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void separate_color_rgb(vec4 col, out float r, out float g, out float b) +{ + r = col.r; + g = col.g; + b = col.b; +} + +void separate_color_hsv(vec4 col, out float r, out float g, out float b) +{ + vec4 hsv; + + rgb_to_hsv(col, hsv); + r = hsv[0]; + g = hsv[1]; + b = hsv[2]; +} + +void separate_color_hsl(vec4 col, out float r, out float g, out float b) +{ + vec4 hsl; + + rgb_to_hsl(col, hsl); + r = hsl[0]; + g = hsl[1]; + b = hsl[2]; +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl index 180e0fd1940..8e475ec39a7 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) void separate_hsv(vec4 col, out float h, out float s, out float v) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl index edc2fa32177..8d9773913ff 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl @@ -1,5 +1,5 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) vec2 calc_brick_texture(vec3 p, float mortar_size, diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl index da131978f72..cf7d6ae18e6 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) void node_tex_environment_equirectangular(vec3 co, out vec3 uv) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl index 1552a2facc3..961fe23e67e 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl) /* 1D Musgrave fBm diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl index c90b2211dcf..3df6f7b29fb 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_fractal_noise.glsl) diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl index dd12b602edf..0fb8ef15f5f 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl @@ -1,5 +1,5 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) /* * Original code is under the MIT License, Copyright (c) 2013 Inigo Quilez. diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl index eed98232d0b..c24a9417219 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_fractal_noise.glsl) diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl index 030b58f0736..c5081372aa4 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) /* White Noise */ diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_transform_utils.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_transform_utils.glsl new file mode 100644 index 00000000000..87048e5c5d6 --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_transform_utils.glsl @@ -0,0 +1,71 @@ +void normal_transform_object_to_world(vec3 vin, out vec3 vout) +{ + vout = normal_object_to_world(vin); +} + +void normal_transform_world_to_object(vec3 vin, out vec3 vout) +{ + vout = normal_world_to_object(vin); +} + +void direction_transform_object_to_world(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ModelMatrix, vin); +} + +void direction_transform_object_to_view(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ModelMatrix, vin); + vout = transform_direction(ViewMatrix, vout); +} + +void direction_transform_view_to_world(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ViewMatrixInverse, vin); +} + +void direction_transform_view_to_object(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ViewMatrixInverse, vin); + vout = transform_direction(ModelMatrixInverse, vout); +} + +void direction_transform_world_to_view(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ViewMatrix, vin); +} + +void direction_transform_world_to_object(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ModelMatrixInverse, vin); +} + +void point_transform_object_to_world(vec3 vin, out vec3 vout) +{ + vout = point_object_to_world(vin); +} + +void point_transform_object_to_view(vec3 vin, out vec3 vout) +{ + vout = point_object_to_view(vin); +} + +void point_transform_view_to_world(vec3 vin, out vec3 vout) +{ + vout = point_view_to_world(vin); +} + +void point_transform_view_to_object(vec3 vin, out vec3 vout) +{ + vout = point_view_to_object(vin); +} + +void point_transform_world_to_view(vec3 vin, out vec3 vout) +{ + vout = point_world_to_view(vin); +} + +void point_transform_world_to_object(vec3 vin, out vec3 vout) +{ + vout = point_world_to_object(vin); +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl deleted file mode 100644 index f6dec1b24e2..00000000000 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl +++ /dev/null @@ -1,41 +0,0 @@ -/* ext is vec4(in_x, in_dy, out_x, out_dy). */ -float curve_vec_extrapolate(float x, float y, vec4 ext) -{ - if (x < 0.0) { - return y + x * ext.y; - } - else if (x > 1.0) { - return y + (x - 1.0) * ext.w; - } - else { - return y; - } -} - -#define RANGE_RESCALE(x, min, range) ((x - min) * range) - -void curves_vec(float fac, - vec3 vec, - sampler1DArray curvemap, - float layer, - vec3 range, - vec4 ext_x, - vec4 ext_y, - vec4 ext_z, - out vec3 outvec) -{ - vec4 co = vec4(vec, layer); - - vec3 xyz_min = vec3(ext_x.x, ext_y.x, ext_z.x); - co.xyz = RANGE_RESCALE(co.xyz, xyz_min, range); - - outvec.x = texture(curvemap, co.xw).x; - outvec.y = texture(curvemap, co.yw).y; - outvec.z = texture(curvemap, co.zw).z; - - outvec.x = curve_vec_extrapolate(co.x, outvec.r, ext_x); - outvec.y = curve_vec_extrapolate(co.y, outvec.g, ext_y); - outvec.z = curve_vec_extrapolate(co.z, outvec.b, ext_z); - - outvec = mix(vec, outvec, fac); -} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl index 8f6bf17f195..018784c42a5 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) void vector_math_add(vec3 a, vec3 b, vec3 c, float scale, out vec3 outVector, out float outValue) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl index ff0fb1c0418..8f7bd26ca18 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) vec3 rotate_around_axis(vec3 p, vec3 axis, float angle) { diff --git a/source/blender/gpu/tests/gpu_shader_builtin_test.cc b/source/blender/gpu/tests/gpu_shader_builtin_test.cc index 6ef8a032a73..5dc70a8bf0f 100644 --- a/source/blender/gpu/tests/gpu_shader_builtin_test.cc +++ b/source/blender/gpu/tests/gpu_shader_builtin_test.cc @@ -35,6 +35,7 @@ static void test_shader_builtin() test_compile_builtin_shader(GPU_SHADER_2D_UNIFORM_COLOR, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_FLAT_COLOR, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_SMOOTH_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_3D_IMAGE, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_IMAGE, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_COLOR, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_DESATURATE_COLOR, GPU_SHADER_CFG_DEFAULT); diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 0390df06052..7493fa3e4af 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -109,6 +109,14 @@ struct ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[I /** * + * \attention Defined in readimage.c + */ +struct ImBuf *IMB_thumb_load_image(const char *filepath, + const size_t max_thumb_size, + char colorspace[IM_MAX_SPACE]); + +/** + * * \attention Defined in allocimbuf.c */ void IMB_freeImBuf(struct ImBuf *ibuf); @@ -522,7 +530,6 @@ void IMB_scaleImBuf_threaded(struct ImBuf *ibuf, unsigned int newx, unsigned int * \attention Defined in writeimage.c */ bool IMB_saveiff(struct ImBuf *ibuf, const char *filepath, int flags); -bool IMB_prepare_write_ImBuf(bool isfloat, struct ImBuf *ibuf); /** * @@ -546,12 +553,6 @@ bool IMB_isanim(const char *filepath); int imb_get_anim_type(const char *filepath); /** - * - * \attention Defined in util.c - */ -bool IMB_isfloat(const struct ImBuf *ibuf); - -/** * Test if color-space conversions of pixels in buffer need to take into account alpha. */ bool IMB_alpha_affects_rgb(const struct ImBuf *ibuf); diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h index 934163846e4..16cf0e2125e 100644 --- a/source/blender/imbuf/IMB_imbuf_types.h +++ b/source/blender/imbuf/IMB_imbuf_types.h @@ -47,7 +47,7 @@ typedef struct DDSData { * Also; add new variables to the end to save pain! */ -/* Warning: Keep explicit value assignments here, +/* WARNING: Keep explicit value assignments here, * this file is included in areas where not all format defines are set * (e.g. intern/dds only get WITH_DDS, even if TIFF, HDR etc are also defined). * See T46524. */ diff --git a/source/blender/imbuf/IMB_thumbs.h b/source/blender/imbuf/IMB_thumbs.h index 623a3b2b5f4..b55a6f653b8 100644 --- a/source/blender/imbuf/IMB_thumbs.h +++ b/source/blender/imbuf/IMB_thumbs.h @@ -50,7 +50,7 @@ typedef enum ThumbSource { /** * Create thumbnail for file and returns new imbuf for thumbnail. */ -struct ImBuf *IMB_thumb_create(const char *path, +struct ImBuf *IMB_thumb_create(const char *filepath, ThumbSize size, ThumbSource source, struct ImBuf *img); @@ -58,17 +58,17 @@ struct ImBuf *IMB_thumb_create(const char *path, /** * Read thumbnail for file and returns new imbuf for thumbnail. */ -struct ImBuf *IMB_thumb_read(const char *path, ThumbSize size); +struct ImBuf *IMB_thumb_read(const char *filepath, ThumbSize size); /** * Delete all thumbs for the file. */ -void IMB_thumb_delete(const char *path, ThumbSize size); +void IMB_thumb_delete(const char *filepath, ThumbSize size); /** * Create the thumb if necessary and manage failed and old thumbs. */ -struct ImBuf *IMB_thumb_manage(const char *path, ThumbSize size, ThumbSource source); +struct ImBuf *IMB_thumb_manage(const char *filepath, ThumbSize size, ThumbSource source); /** * Create the necessary directories to store the thumbnails. @@ -85,7 +85,7 @@ struct ImBuf *IMB_thumb_load_blend(const char *blen_path, /** * Special function for previewing fonts. */ -struct ImBuf *IMB_thumb_load_font(const char *filename, unsigned int x, unsigned int y); +struct ImBuf *IMB_thumb_load_font(const char *filepath, unsigned int x, unsigned int y); bool IMB_thumb_load_font_get_hash(char *r_hash); void IMB_thumb_clear_translations(void); void IMB_thumb_ensure_translations(void); diff --git a/source/blender/imbuf/intern/IMB_filetype.h b/source/blender/imbuf/intern/IMB_filetype.h index 31f8b3a9505..67d1aefeacb 100644 --- a/source/blender/imbuf/intern/IMB_filetype.h +++ b/source/blender/imbuf/intern/IMB_filetype.h @@ -36,6 +36,17 @@ typedef struct ImFileType { char colorspace[IM_MAX_SPACE]); /** Load an image from a file. */ struct ImBuf *(*load_filepath)(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]); + /** + * Load/Create a thumbnail image from a filepath. `max_thumb_size` is maximum size of either + * dimension, so can return less on either or both. Should, if possible and performant, return + * dimensions of the full-size image in r_width & r_height. + */ + struct ImBuf *(*load_filepath_thumbnail)(const char *filepath, + const int flags, + const size_t max_thumb_size, + char colorspace[IM_MAX_SPACE], + size_t *r_width, + size_t *r_height); /** Save to a file (or memory if #IB_mem is set in `flags` and the format supports it). */ bool (*save)(struct ImBuf *ibuf, const char *filepath, int flags); void (*load_tile)(struct ImBuf *ibuf, @@ -143,6 +154,12 @@ struct ImBuf *imb_load_jpeg(const unsigned char *buffer, size_t size, int flags, char colorspace[IM_MAX_SPACE]); +struct ImBuf *imb_thumbnail_jpeg(const char *filepath, + const int flags, + const size_t max_thumb_size, + char colorspace[IM_MAX_SPACE], + size_t *r_width, + size_t *r_height); /** \} */ diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 096089d4c41..0052ce19aa1 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -423,7 +423,7 @@ static int startavi(struct anim *anim) anim->cur_position = 0; # if 0 - printf("x:%d y:%d size:%d interl:%d dur:%d\n", + printf("x:%d y:%d size:%d interlace:%d dur:%d\n", anim->x, anim->y, anim->framesize, diff --git a/source/blender/imbuf/intern/cineon/cineon_dpx.c b/source/blender/imbuf/intern/cineon/cineon_dpx.c index d1cd30cfe84..6448d6cd76a 100644 --- a/source/blender/imbuf/intern/cineon/cineon_dpx.c +++ b/source/blender/imbuf/intern/cineon/cineon_dpx.c @@ -69,7 +69,7 @@ static struct ImBuf *imb_load_dpx_cineon(const unsigned char *mem, return ibuf; } -static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filename, int use_cineon, int flags) +static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filepath, int use_cineon, int flags) { LogImageFile *logImage; float *fbuf; @@ -86,7 +86,7 @@ static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filename, int use_cineon depth = (ibuf->planes + 7) >> 3; if (depth > 4 || depth < 3) { - printf("DPX/Cineon: unsupported depth: %d for file: '%s'\n", depth, filename); + printf("DPX/Cineon: unsupported depth: %d for file: '%s'\n", depth, filepath); return 0; } @@ -103,7 +103,7 @@ static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filename, int use_cineon bitspersample = 8; } - logImage = logImageCreate(filename, + logImage = logImageCreate(filepath, use_cineon, ibuf->x, ibuf->y, diff --git a/source/blender/imbuf/intern/cineon/cineonlib.c b/source/blender/imbuf/intern/cineon/cineonlib.c index 3bdfcb60292..8312476bda0 100644 --- a/source/blender/imbuf/intern/cineon/cineonlib.c +++ b/source/blender/imbuf/intern/cineon/cineonlib.c @@ -35,7 +35,7 @@ void cineonSetVerbose(int verbosity) static void fillCineonMainHeader(LogImageFile *cineon, CineonMainHeader *header, - const char *filename, + const char *filepath, const char *creator) { time_t fileClock; @@ -57,7 +57,7 @@ static void fillCineonMainHeader(LogImageFile *cineon, getRowLength(cineon->width, cineon->element[0]), cineon->isMSB); strcpy(header->fileHeader.version, "v4.5"); - strncpy(header->fileHeader.file_name, filename, 99); + strncpy(header->fileHeader.file_name, filepath, 99); header->fileHeader.file_name[99] = 0; fileClock = time(NULL); fileTime = localtime(&fileClock); @@ -126,7 +126,7 @@ LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t { CineonMainHeader header; LogImageFile *cineon = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__); - const char *filename = (const char *)byteStuff; + const char *filepath = (const char *)byteStuff; int i; unsigned int dataOffset; @@ -144,11 +144,11 @@ LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t cineon->file = NULL; if (fromMemory == 0) { - /* byteStuff is then the filename */ - cineon->file = BLI_fopen(filename, "rb"); + /* byteStuff is then the filepath */ + cineon->file = BLI_fopen(filepath, "rb"); if (cineon->file == NULL) { if (verbose) { - printf("Cineon: Failed to open file \"%s\".\n", filename); + printf("Cineon: Failed to open file \"%s\".\n", filepath); } logImageClose(cineon); return NULL; @@ -350,7 +350,7 @@ LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t } LogImageFile *cineonCreate( - const char *filename, int width, int height, int bitsPerSample, const char *creator) + const char *filepath, int width, int height, int bitsPerSample, const char *creator) { CineonMainHeader header; const char *shortFilename = NULL; @@ -393,18 +393,18 @@ LogImageFile *cineonCreate( cineon->referenceBlack = 95.0f; cineon->gamma = 1.7f; - shortFilename = strrchr(filename, '/'); + shortFilename = strrchr(filepath, PATHSEP_CHAR); if (shortFilename == NULL) { - shortFilename = filename; + shortFilename = filepath; } else { shortFilename++; } - cineon->file = BLI_fopen(filename, "wb"); + cineon->file = BLI_fopen(filepath, "wb"); if (cineon->file == NULL) { if (verbose) { - printf("cineon: Couldn't open file %s\n", filename); + printf("cineon: Couldn't open file %s\n", filepath); } logImageClose(cineon); return NULL; diff --git a/source/blender/imbuf/intern/cineon/cineonlib.h b/source/blender/imbuf/intern/cineon/cineonlib.h index 37b27d19539..13d40461728 100644 --- a/source/blender/imbuf/intern/cineon/cineonlib.h +++ b/source/blender/imbuf/intern/cineon/cineonlib.h @@ -114,7 +114,7 @@ typedef struct { void cineonSetVerbose(int); LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t bufferSize); LogImageFile *cineonCreate( - const char *filename, int width, int height, int bitsPerSample, const char *creator); + const char *filepath, int width, int height, int bitsPerSample, const char *creator); #ifdef __cplusplus } diff --git a/source/blender/imbuf/intern/cineon/dpxlib.c b/source/blender/imbuf/intern/cineon/dpxlib.c index 2d28a477c8a..28c19116361 100644 --- a/source/blender/imbuf/intern/cineon/dpxlib.c +++ b/source/blender/imbuf/intern/cineon/dpxlib.c @@ -124,7 +124,7 @@ LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t buf { DpxMainHeader header; LogImageFile *dpx = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__); - const char *filename = (const char *)byteStuff; + const char *filepath = (const char *)byteStuff; int i; if (dpx == NULL) { @@ -141,11 +141,11 @@ LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t buf dpx->file = NULL; if (fromMemory == 0) { - /* byteStuff is then the filename */ - dpx->file = BLI_fopen(filename, "rb"); + /* byteStuff is then the filepath */ + dpx->file = BLI_fopen(filepath, "rb"); if (dpx->file == NULL) { if (verbose) { - printf("DPX: Failed to open file \"%s\".\n", filename); + printf("DPX: Failed to open file \"%s\".\n", filepath); } logImageClose(dpx); return NULL; @@ -406,7 +406,7 @@ LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t buf return dpx; } -LogImageFile *dpxCreate(const char *filename, +LogImageFile *dpxCreate(const char *filepath, int width, int height, int bitsPerSample, @@ -502,19 +502,19 @@ LogImageFile *dpxCreate(const char *filename, dpx->gamma = 1.7f; } - shortFilename = strrchr(filename, '/'); + shortFilename = strrchr(filepath, PATHSEP_CHAR); if (shortFilename == NULL) { - shortFilename = filename; + shortFilename = filepath; } else { shortFilename++; } - dpx->file = BLI_fopen(filename, "wb"); + dpx->file = BLI_fopen(filepath, "wb"); if (dpx->file == NULL) { if (verbose) { - printf("DPX: Couldn't open file %s\n", filename); + printf("DPX: Couldn't open file %s\n", filepath); } logImageClose(dpx); return NULL; diff --git a/source/blender/imbuf/intern/cineon/dpxlib.h b/source/blender/imbuf/intern/cineon/dpxlib.h index d8ed5dc6f67..aac424d52d6 100644 --- a/source/blender/imbuf/intern/cineon/dpxlib.h +++ b/source/blender/imbuf/intern/cineon/dpxlib.h @@ -133,7 +133,7 @@ typedef struct { void dpxSetVerbose(int verbosity); LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t bufferSize); -LogImageFile *dpxCreate(const char *filename, +LogImageFile *dpxCreate(const char *filepath, int width, int height, int bitsPerSample, diff --git a/source/blender/imbuf/intern/cineon/logImageCore.c b/source/blender/imbuf/intern/cineon/logImageCore.c index 36e12e07316..e693aa6f891 100644 --- a/source/blender/imbuf/intern/cineon/logImageCore.c +++ b/source/blender/imbuf/intern/cineon/logImageCore.c @@ -101,10 +101,10 @@ int logImageIsCineon(const void *buffer, const unsigned int size) return (magicNum == CINEON_FILE_MAGIC || magicNum == swap_uint(CINEON_FILE_MAGIC, 1)); } -LogImageFile *logImageOpenFromFile(const char *filename, int cineon) +LogImageFile *logImageOpenFromFile(const char *filepath, int cineon) { unsigned int magicNum; - FILE *f = BLI_fopen(filename, "rb"); + FILE *f = BLI_fopen(filepath, "rb"); (void)cineon; @@ -120,10 +120,10 @@ LogImageFile *logImageOpenFromFile(const char *filename, int cineon) fclose(f); if (logImageIsDpx(&magicNum, sizeof(magicNum))) { - return dpxOpen((const unsigned char *)filename, 0, 0); + return dpxOpen((const unsigned char *)filepath, 0, 0); } if (logImageIsCineon(&magicNum, sizeof(magicNum))) { - return cineonOpen((const unsigned char *)filename, 0, 0); + return cineonOpen((const unsigned char *)filepath, 0, 0); } return NULL; @@ -141,7 +141,7 @@ LogImageFile *logImageOpenFromMemory(const unsigned char *buffer, unsigned int s return NULL; } -LogImageFile *logImageCreate(const char *filename, +LogImageFile *logImageCreate(const char *filepath, int cineon, int width, int height, @@ -155,10 +155,10 @@ LogImageFile *logImageCreate(const char *filename, { /* referenceWhite, referenceBlack and gamma values are only supported for DPX file */ if (cineon) { - return cineonCreate(filename, width, height, bitsPerSample, creator); + return cineonCreate(filepath, width, height, bitsPerSample, creator); } - return dpxCreate(filename, + return dpxCreate(filepath, width, height, bitsPerSample, diff --git a/source/blender/imbuf/intern/cineon/logImageCore.h b/source/blender/imbuf/intern/cineon/logImageCore.h index 6875dba3f87..35540497828 100644 --- a/source/blender/imbuf/intern/cineon/logImageCore.h +++ b/source/blender/imbuf/intern/cineon/logImageCore.h @@ -19,6 +19,12 @@ #include "BLI_sys_types.h" #include "BLI_utildefines.h" +#ifdef _WIN32 +# define PATHSEP_CHAR '\\' +#else +# define PATHSEP_CHAR '/' +#endif + #ifdef __cplusplus extern "C" { #endif @@ -169,9 +175,9 @@ void logImageSetVerbose(int verbosity); int logImageIsDpx(const void *buffer, unsigned int size); int logImageIsCineon(const void *buffer, unsigned int size); LogImageFile *logImageOpenFromMemory(const unsigned char *buffer, unsigned int size); -LogImageFile *logImageOpenFromFile(const char *filename, int cineon); +LogImageFile *logImageOpenFromFile(const char *filepath, int cineon); void logImageGetSize(LogImageFile *logImage, int *width, int *height, int *depth); -LogImageFile *logImageCreate(const char *filename, +LogImageFile *logImageCreate(const char *filepath, int cineon, int width, int height, diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 53aa74edc61..1613595148b 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -2445,68 +2445,77 @@ void IMB_colormanagement_imbuf_make_display_space( colormanagement_imbuf_make_display_space(ibuf, view_settings, display_settings, false); } +static ImBuf *imbuf_ensure_editable(ImBuf *ibuf, ImBuf *colormanaged_ibuf, bool allocate_result) +{ + if (colormanaged_ibuf != ibuf) { + /* Is already an editable copy. */ + return colormanaged_ibuf; + } + + if (allocate_result) { + /* Copy full image buffer. */ + colormanaged_ibuf = IMB_dupImBuf(ibuf); + IMB_metadata_copy(colormanaged_ibuf, ibuf); + return colormanaged_ibuf; + } + else { + /* Render pipeline is constructing image buffer itself, + * but it's re-using byte and float buffers from render result make copy of this buffers + * here sine this buffers would be transformed to other color space here. */ + if (ibuf->rect && (ibuf->mall & IB_rect) == 0) { + ibuf->rect = MEM_dupallocN(ibuf->rect); + ibuf->mall |= IB_rect; + } + + if (ibuf->rect_float && (ibuf->mall & IB_rectfloat) == 0) { + ibuf->rect_float = MEM_dupallocN(ibuf->rect_float); + ibuf->mall |= IB_rectfloat; + } + + return ibuf; + } +} + ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, bool save_as_render, bool allocate_result, const ImageFormatData *image_format) { ImBuf *colormanaged_ibuf = ibuf; - const bool is_movie = BKE_imtype_is_movie(image_format->imtype); - const bool requires_linear_float = BKE_imtype_requires_linear_float(image_format->imtype); - const bool do_alpha_under = image_format->planes != R_IMF_PLANES_RGBA; + /* Update byte buffer if exists but invalid. */ if (ibuf->rect_float && ibuf->rect && (ibuf->userflags & (IB_DISPLAY_BUFFER_INVALID | IB_RECT_INVALID)) != 0) { IMB_rect_from_float(ibuf); ibuf->userflags &= ~(IB_RECT_INVALID | IB_DISPLAY_BUFFER_INVALID); } - const bool do_colormanagement_display = save_as_render && (is_movie || !requires_linear_float); - const bool do_colormanagement_linear = save_as_render && requires_linear_float && - image_format->linear_colorspace_settings.name[0] && - !IMB_colormanagement_space_name_is_scene_linear( - image_format->linear_colorspace_settings.name); - - if (do_colormanagement_display || do_colormanagement_linear || do_alpha_under) { - if (allocate_result) { - colormanaged_ibuf = IMB_dupImBuf(ibuf); - } - else { - /* Render pipeline is constructing image buffer itself, - * but it's re-using byte and float buffers from render result make copy of this buffers - * here sine this buffers would be transformed to other color space here. - */ + /* Detect if we are writing to a file format that needs a linear float buffer. */ + const bool linear_float_output = BKE_imtype_requires_linear_float(image_format->imtype); - if (ibuf->rect && (ibuf->mall & IB_rect) == 0) { - ibuf->rect = MEM_dupallocN(ibuf->rect); - ibuf->mall |= IB_rect; - } + /* Detect if we are writing output a byte buffer, which we would need to create + * with color management conversions applied. This may be for either applying the + * display transform for renders, or a user specified color space for the file. */ + const bool byte_output = BKE_image_format_is_byte(image_format); - if (ibuf->rect_float && (ibuf->mall & IB_rectfloat) == 0) { - ibuf->rect_float = MEM_dupallocN(ibuf->rect_float); - ibuf->mall |= IB_rectfloat; - } - } - } + BLI_assert(!(byte_output && linear_float_output)); - /* If we're saving from RGBA to RGB buffer then it's not - * so much useful to just ignore alpha -- it leads to bad - * artifacts especially when saving byte images. - * - * What we do here is we're overlaying our image on top of - * background color (which is currently black). + /* If we're saving from RGBA to RGB buffer then it's not so much useful to just ignore alpha -- + * it leads to bad artifacts especially when saving byte images. * - * This is quite much the same as what Gimp does and it - * seems to be what artists expects from saving. + * What we do here is we're overlaying our image on top of background color (which is currently + * black). This is quite much the same as what Gimp does and it seems to be what artists expects + * from saving. * - * Do a conversion here, so image format writers could - * happily assume all the alpha tricks were made already. - * helps keep things locally here, not spreading it to - * all possible image writers we've got. + * Do a conversion here, so image format writers could happily assume all the alpha tricks were + * made already. helps keep things locally here, not spreading it to all possible image writers + * we've got. */ - if (do_alpha_under) { + if (image_format->planes != R_IMF_PLANES_RGBA) { float color[3] = {0, 0, 0}; + colormanaged_ibuf = imbuf_ensure_editable(ibuf, colormanaged_ibuf, allocate_result); + if (colormanaged_ibuf->rect_float && colormanaged_ibuf->channels == 4) { IMB_alpha_under_color_float( colormanaged_ibuf->rect_float, colormanaged_ibuf->x, colormanaged_ibuf->y, color); @@ -2520,69 +2529,95 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, } } - if (do_colormanagement_display) { - /* Color management with display and view transform. */ - bool make_byte = false; + if (save_as_render && !linear_float_output) { + /* Render output: perform conversion to display space using view transform. */ + colormanaged_ibuf = imbuf_ensure_editable(ibuf, colormanaged_ibuf, allocate_result); - /* for proper check whether byte buffer is required by a format or not - * should be pretty safe since this image buffer is supposed to be used for - * saving only and ftype would be overwritten a bit later by BKE_imbuf_write - */ - colormanaged_ibuf->ftype = BKE_imtype_to_ftype(image_format->imtype, - &colormanaged_ibuf->foptions); - - /* if file format isn't able to handle float buffer itself, - * we need to allocate byte buffer and store color managed - * image there - */ - const ImFileType *type = IMB_file_type_from_ibuf(colormanaged_ibuf); - if (type != NULL) { - if ((type->save != NULL) && (type->flag & IM_FTYPE_FLOAT) == 0) { - make_byte = true; - } - } - - /* perform color space conversions */ colormanagement_imbuf_make_display_space(colormanaged_ibuf, &image_format->view_settings, &image_format->display_settings, - make_byte); + byte_output); if (colormanaged_ibuf->rect_float) { - /* float buffer isn't linear anymore, + /* Float buffer isn't linear anymore, * image format write callback should check for this flag and assume - * no space conversion should happen if ibuf->float_colorspace != NULL - */ + * no space conversion should happen if ibuf->float_colorspace != NULL. */ colormanaged_ibuf->float_colorspace = display_transform_get_colorspace( &image_format->view_settings, &image_format->display_settings); + if (byte_output) { + colormanaged_ibuf->rect_colorspace = colormanaged_ibuf->float_colorspace; + } } } - else if (do_colormanagement_linear) { - /* Color management transform to another linear color space. */ - if (!colormanaged_ibuf->rect_float) { - IMB_float_from_rect(colormanaged_ibuf); - imb_freerectImBuf(colormanaged_ibuf); + else { + /* Linear render or regular file output: conversion between two color spaces. */ + + /* Detect which color space we need to convert between. */ + const char *from_colorspace = (ibuf->rect_float && !(byte_output && ibuf->rect)) ? + /* From float buffer. */ + (ibuf->float_colorspace) ? ibuf->float_colorspace->name : + global_role_scene_linear : + /* From byte buffer. */ + (ibuf->rect_colorspace) ? ibuf->rect_colorspace->name : + global_role_default_byte; + + const char *to_colorspace = image_format->linear_colorspace_settings.name; + + /* TODO: can we check with OCIO if color spaces are the same but have different names? */ + if (to_colorspace[0] == '\0' || STREQ(from_colorspace, to_colorspace)) { + /* No conversion needed, but may still need to allocate byte buffer for output. */ + if (byte_output && !ibuf->rect) { + ibuf->rect_colorspace = ibuf->float_colorspace; + IMB_rect_from_float(ibuf); + } } + else { + /* Color space conversion needed. */ + colormanaged_ibuf = imbuf_ensure_editable(ibuf, colormanaged_ibuf, allocate_result); + + if (byte_output) { + colormanaged_ibuf->rect_colorspace = colormanage_colorspace_get_named(to_colorspace); + + if (colormanaged_ibuf->rect) { + /* Byte to byte. */ + IMB_colormanagement_transform_byte_threaded((unsigned char *)colormanaged_ibuf->rect, + colormanaged_ibuf->x, + colormanaged_ibuf->y, + colormanaged_ibuf->channels, + from_colorspace, + to_colorspace); + } + else { + /* Float to byte. */ + IMB_rect_from_float(colormanaged_ibuf); + } + } + else { + if (!colormanaged_ibuf->rect_float) { + /* Byte to float. */ + IMB_float_from_rect(colormanaged_ibuf); + imb_freerectImBuf(colormanaged_ibuf); - if (colormanaged_ibuf->rect_float) { - const char *from_colorspace = (ibuf->float_colorspace) ? ibuf->float_colorspace->name : - global_role_scene_linear; - const char *to_colorspace = image_format->linear_colorspace_settings.name; + /* This conversion always goes to scene linear. */ + from_colorspace = global_role_scene_linear; + } - IMB_colormanagement_transform(colormanaged_ibuf->rect_float, - colormanaged_ibuf->x, - colormanaged_ibuf->y, - colormanaged_ibuf->channels, - from_colorspace, - to_colorspace, - false); + if (colormanaged_ibuf->rect_float) { + /* Float to float. */ + IMB_colormanagement_transform(colormanaged_ibuf->rect_float, + colormanaged_ibuf->x, + colormanaged_ibuf->y, + colormanaged_ibuf->channels, + from_colorspace, + to_colorspace, + false); + + colormanaged_ibuf->float_colorspace = colormanage_colorspace_get_named(to_colorspace); + } + } } } - if (colormanaged_ibuf != ibuf) { - IMB_metadata_copy(colormanaged_ibuf, ibuf); - } - return colormanaged_ibuf; } diff --git a/source/blender/imbuf/intern/dds/Stream.cpp b/source/blender/imbuf/intern/dds/Stream.cpp index 34f3654aa3f..566891dac8b 100644 --- a/source/blender/imbuf/intern/dds/Stream.cpp +++ b/source/blender/imbuf/intern/dds/Stream.cpp @@ -45,7 +45,7 @@ unsigned int mem_read(Stream &mem, unsigned long long &i) mem.set_failed(msg_error_seek); return 0; } - memcpy(&i, mem.mem + mem.pos, 8); /* @@ todo: make sure little endian */ + memcpy(&i, mem.mem + mem.pos, 8); /* TODO: make sure little endian. */ mem.pos += 8; return 8; } @@ -56,7 +56,7 @@ unsigned int mem_read(Stream &mem, unsigned int &i) mem.set_failed(msg_error_read); return 0; } - memcpy(&i, mem.mem + mem.pos, 4); /* @@ todo: make sure little endian */ + memcpy(&i, mem.mem + mem.pos, 4); /* TODO: make sure little endian. */ mem.pos += 4; return 4; } @@ -67,7 +67,7 @@ unsigned int mem_read(Stream &mem, unsigned short &i) mem.set_failed(msg_error_read); return 0; } - memcpy(&i, mem.mem + mem.pos, 2); /* @@ todo: make sure little endian */ + memcpy(&i, mem.mem + mem.pos, 2); /* TODO: make sure little endian. */ mem.pos += 2; return 2; } diff --git a/source/blender/imbuf/intern/filetype.c b/source/blender/imbuf/intern/filetype.c index 548bc9e120c..74042ef75be 100644 --- a/source/blender/imbuf/intern/filetype.c +++ b/source/blender/imbuf/intern/filetype.c @@ -33,6 +33,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_jpeg, .load = imb_load_jpeg, .load_filepath = NULL, + .load_filepath_thumbnail = imb_thumbnail_jpeg, .save = imb_savejpeg, .load_tile = NULL, .flag = 0, @@ -45,6 +46,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_png, .load = imb_loadpng, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savepng, .load_tile = NULL, .flag = 0, @@ -57,6 +59,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_bmp, .load = imb_bmp_decode, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savebmp, .load_tile = NULL, .flag = 0, @@ -69,6 +72,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_targa, .load = imb_loadtarga, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savetarga, .load_tile = NULL, .flag = 0, @@ -81,6 +85,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_iris, .load = imb_loadiris, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_saveiris, .load_tile = NULL, .flag = 0, @@ -94,6 +99,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_dpx, .load = imb_load_dpx, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_save_dpx, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -106,6 +112,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_cineon, .load = imb_load_cineon, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_save_cineon, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -120,6 +127,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_tiff, .load = imb_loadtiff, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savetiff, .load_tile = imb_loadtiletiff, .flag = 0, @@ -134,6 +142,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_hdr, .load = imb_loadhdr, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savehdr, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -148,6 +157,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_openexr, .load = imb_load_openexr, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_save_openexr, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -162,6 +172,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_jp2, .load = imb_load_jp2, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_save_jp2, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -176,6 +187,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_dds, .load = imb_load_dds, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = NULL, .load_tile = NULL, .flag = 0, @@ -190,6 +202,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_photoshop, .load = NULL, .load_filepath = imb_load_photoshop, + .load_filepath_thumbnail = NULL, .save = NULL, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -204,6 +217,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_webp, .load = imb_loadwebp, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savewebp, .load_tile = NULL, .flag = 0, @@ -211,7 +225,7 @@ const ImFileType IMB_FILE_TYPES[] = { .default_save_role = COLOR_ROLE_DEFAULT_BYTE, }, #endif - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0}, + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0}, }; const ImFileType *IMB_FILE_TYPES_LAST = &IMB_FILE_TYPES[ARRAY_SIZE(IMB_FILE_TYPES) - 1]; diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 1cc91d25d2a..cbc5d984755 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -376,10 +376,10 @@ int IMB_timecode_to_array_index(IMB_Timecode_Type tc) static void get_index_dir(struct anim *anim, char *index_dir, size_t index_dir_len) { if (!anim->index_dir[0]) { - char fname[FILE_MAXFILE]; - BLI_split_dirfile(anim->name, index_dir, fname, index_dir_len, sizeof(fname)); + char filename[FILE_MAXFILE]; + BLI_split_dirfile(anim->name, index_dir, filename, index_dir_len, sizeof(filename)); BLI_path_append(index_dir, index_dir_len, "BL_proxy"); - BLI_path_append(index_dir, index_dir_len, fname); + BLI_path_append(index_dir, index_dir_len, filename); } else { BLI_strncpy(index_dir, anim->index_dir, index_dir_len); @@ -388,14 +388,14 @@ static void get_index_dir(struct anim *anim, char *index_dir, size_t index_dir_l void IMB_anim_get_fname(struct anim *anim, char *file, int size) { - char fname[FILE_MAXFILE]; - BLI_split_dirfile(anim->name, file, fname, size, sizeof(fname)); - BLI_strncpy(file, fname, size); + char filename[FILE_MAXFILE]; + BLI_split_dirfile(anim->name, file, filename, size, sizeof(filename)); + BLI_strncpy(file, filename, size); } -static bool get_proxy_filename(struct anim *anim, +static bool get_proxy_filepath(struct anim *anim, IMB_Proxy_Size preview_size, - char *fname, + char *filepath, bool temp) { char index_dir[FILE_MAXDIR]; @@ -426,11 +426,11 @@ static bool get_proxy_filename(struct anim *anim, return false; } - BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir, proxy_name); + BLI_join_dirfile(filepath, FILE_MAXFILE + FILE_MAXDIR, index_dir, proxy_name); return true; } -static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *fname) +static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *filepath) { char index_dir[FILE_MAXDIR]; int i = IMB_timecode_to_array_index(tc); @@ -457,7 +457,7 @@ static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *fname get_index_dir(anim, index_dir, sizeof(index_dir)); - BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir, index_name); + BLI_join_dirfile(filepath, FILE_MAXFILE + FILE_MAXDIR, index_dir, index_name); } /* ---------------------------------------------------------------------- @@ -492,18 +492,18 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( { struct proxy_output_ctx *rv = MEM_callocN(sizeof(struct proxy_output_ctx), "alloc_proxy_output"); - char fname[FILE_MAX]; + char filepath[FILE_MAX]; rv->proxy_size = proxy_size; rv->anim = anim; - get_proxy_filename(rv->anim, rv->proxy_size, fname, true); - BLI_make_existing_file(fname); + get_proxy_filepath(rv->anim, rv->proxy_size, filepath, true); + BLI_make_existing_file(filepath); rv->of = avformat_alloc_context(); rv->of->oformat = av_guess_format("avi", NULL, NULL); - rv->of->url = av_strdup(fname); + rv->of->url = av_strdup(filepath); fprintf(stderr, "Starting work on proxy: %s\n", rv->of->url); @@ -577,7 +577,7 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( avcodec_parameters_from_context(rv->st->codecpar, rv->c); - int ret = avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE); + int ret = avio_open(&rv->of->pb, filepath, AVIO_FLAG_WRITE); if (ret < 0) { fprintf(stderr, @@ -723,8 +723,8 @@ static void add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *fr static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback) { - char fname[FILE_MAX]; - char fname_tmp[FILE_MAX]; + char filepath[FILE_MAX]; + char filepath_tmp[FILE_MAX]; if (!ctx) { return; @@ -755,15 +755,15 @@ static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback) av_free(ctx->frame); } - get_proxy_filename(ctx->anim, ctx->proxy_size, fname_tmp, true); + get_proxy_filepath(ctx->anim, ctx->proxy_size, filepath_tmp, true); if (rollback) { - unlink(fname_tmp); + unlink(filepath_tmp); } else { - get_proxy_filename(ctx->anim, ctx->proxy_size, fname, false); - unlink(fname); - BLI_rename(fname_tmp, fname); + get_proxy_filepath(ctx->anim, ctx->proxy_size, filepath, false); + unlink(filepath); + BLI_rename(filepath_tmp, filepath); } MEM_freeN(ctx); @@ -907,11 +907,11 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, for (i = 0; i < num_indexers; i++) { if (tcs_in_use & tc_types[i]) { - char fname[FILE_MAX]; + char filepath[FILE_MAX]; - get_tc_filename(anim, tc_types[i], fname); + get_tc_filename(anim, tc_types[i], filepath); - context->indexer[i] = IMB_index_builder_create(fname); + context->indexer[i] = IMB_index_builder_create(filepath); if (!context->indexer[i]) { tcs_in_use &= ~tc_types[i]; } @@ -1212,7 +1212,7 @@ typedef struct FallbackIndexBuilderContext { } FallbackIndexBuilderContext; static AviMovie *alloc_proxy_output_avi( - struct anim *anim, char *filename, int width, int height, int quality) + struct anim *anim, char *filepath, int width, int height, int quality) { int x, y; AviFormat format; @@ -1233,7 +1233,7 @@ static AviMovie *alloc_proxy_output_avi( format = AVI_FORMAT_MJPEG; - if (AVI_open_compress(filename, avi, 1, format) != AVI_ERROR_NONE) { + if (AVI_open_compress(filepath, avi, 1, format) != AVI_ERROR_NONE) { MEM_freeN(avi); return NULL; } @@ -1275,13 +1275,13 @@ static IndexBuildContext *index_fallback_create_context(struct anim *anim, for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { if (context->proxy_sizes_in_use & proxy_sizes[i]) { - char fname[FILE_MAX]; + char filepath[FILE_MAX]; - get_proxy_filename(anim, proxy_sizes[i], fname, true); - BLI_make_existing_file(fname); + get_proxy_filepath(anim, proxy_sizes[i], filepath, true); + BLI_make_existing_file(filepath); context->proxy_ctx[i] = alloc_proxy_output_avi( - anim, fname, anim->x * proxy_fac[i], anim->y * proxy_fac[i], quality); + anim, filepath, anim->x * proxy_fac[i], anim->y * proxy_fac[i], quality); } } @@ -1291,8 +1291,8 @@ static IndexBuildContext *index_fallback_create_context(struct anim *anim, static void index_rebuild_fallback_finish(FallbackIndexBuilderContext *context, int stop) { struct anim *anim = context->anim; - char fname[FILE_MAX]; - char fname_tmp[FILE_MAX]; + char filepath[FILE_MAX]; + char filepath_tmp[FILE_MAX]; int i; for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { @@ -1300,15 +1300,15 @@ static void index_rebuild_fallback_finish(FallbackIndexBuilderContext *context, AVI_close_compress(context->proxy_ctx[i]); MEM_freeN(context->proxy_ctx[i]); - get_proxy_filename(anim, proxy_sizes[i], fname_tmp, true); - get_proxy_filename(anim, proxy_sizes[i], fname, false); + get_proxy_filepath(anim, proxy_sizes[i], filepath_tmp, true); + get_proxy_filepath(anim, proxy_sizes[i], filepath, false); if (stop) { - unlink(fname_tmp); + unlink(filepath_tmp); } else { - unlink(fname); - rename(fname_tmp, fname); + unlink(filepath); + rename(filepath_tmp, filepath); } } } @@ -1388,7 +1388,7 @@ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Proxy_Size proxy_size = proxy_sizes[i]; if (proxy_size & proxy_sizes_to_build) { char filename[FILE_MAX]; - if (get_proxy_filename(anim, proxy_size, filename, false) == false) { + if (get_proxy_filepath(anim, proxy_size, filename, false) == false) { return NULL; } void **filename_key_p; @@ -1411,7 +1411,7 @@ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Proxy_Size proxy_size = proxy_sizes[i]; if (proxy_size & built_proxies) { char filename[FILE_MAX]; - if (get_proxy_filename(anim, proxy_size, filename, false) == false) { + if (get_proxy_filepath(anim, proxy_size, filename, false) == false) { return NULL; } printf("Skipping proxy: %s\n", filename); @@ -1532,7 +1532,7 @@ void IMB_anim_set_index_dir(struct anim *anim, const char *dir) struct anim *IMB_anim_open_proxy(struct anim *anim, IMB_Proxy_Size preview_size) { - char fname[FILE_MAX]; + char filepath[FILE_MAX]; int i = IMB_proxy_size_to_array_index(preview_size); if (i < 0) { @@ -1547,10 +1547,10 @@ struct anim *IMB_anim_open_proxy(struct anim *anim, IMB_Proxy_Size preview_size) return NULL; } - get_proxy_filename(anim, preview_size, fname, false); + get_proxy_filepath(anim, preview_size, filepath, false); /* proxies are generated in the same color space as animation itself */ - anim->proxy_anim[i] = IMB_open_anim(fname, 0, 0, anim->colorspace); + anim->proxy_anim[i] = IMB_open_anim(filepath, 0, 0, anim->colorspace); anim->proxies_tried |= preview_size; @@ -1559,7 +1559,7 @@ struct anim *IMB_anim_open_proxy(struct anim *anim, IMB_Proxy_Size preview_size) struct anim_index *IMB_anim_open_index(struct anim *anim, IMB_Timecode_Type tc) { - char fname[FILE_MAX]; + char filepath[FILE_MAX]; int i = IMB_timecode_to_array_index(tc); if (i < 0) { @@ -1574,9 +1574,9 @@ struct anim_index *IMB_anim_open_index(struct anim *anim, IMB_Timecode_Type tc) return NULL; } - get_tc_filename(anim, tc, fname); + get_tc_filename(anim, tc, filepath); - anim->curr_idx[i] = IMB_indexer_open(fname); + anim->curr_idx[i] = IMB_indexer_open(filepath); anim->indices_tried |= tc; @@ -1602,7 +1602,7 @@ IMB_Proxy_Size IMB_anim_proxy_get_existing(struct anim *anim) for (i = 0; i < num_proxy_sizes; i++) { IMB_Proxy_Size proxy_size = proxy_sizes[i]; char filename[FILE_MAX]; - get_proxy_filename(anim, proxy_size, filename, false); + get_proxy_filepath(anim, proxy_size, filename, false); if (BLI_exists(filename)) { existing |= proxy_size; } diff --git a/source/blender/imbuf/intern/iris.c b/source/blender/imbuf/intern/iris.c index eb0a2c4a47f..a8150fd1648 100644 --- a/source/blender/imbuf/intern/iris.c +++ b/source/blender/imbuf/intern/iris.c @@ -105,7 +105,7 @@ static int expandrow2( float *optr, const float *optr_end, const uchar *iptr, const uchar *iptr_end, int z); static void interleaverow(uchar *lptr, const uchar *cptr, int z, int n); static void interleaverow2(float *lptr, const uchar *cptr, int z, int n); -static int compressrow(uchar *lbuf, uchar *rlebuf, int z, int row_len); +static int compressrow(const uchar *lbuf, uchar *rlebuf, int z, int row_len); static void lumrow(const uchar *rgbptr, uchar *lumptr, int n); /* @@ -779,18 +779,24 @@ fail: } /** - * Copy an array of ints to an iris image file. - * Each int represents one pixel. xsize and ysize specify the dimensions of - * the pixel array. zsize specifies what kind of image file to - * write out. if zsize is 1, the luminance of the pixels are - * calculated, and a single channel black and white image is saved. - * If zsize is 3, an RGB image file is saved. If zsize is 4, an - * RGBA image file is saved. - * - * Added: zbuf write + * \param filepath: The file path to write to. + * \param lptr: an array of integers to an iris image file (each int represents one pixel). + * \param zptr: depth-buffer (optional, may be NULL). + * \param xsize: with width of the pixel-array. + * \param ysize: height of the pixel-array. + * \param zsize: specifies what kind of image file to write out. + * - 1: the luminance of the pixels are calculated, + * and a single channel black and white image is saved. + * - 3: an RGB image file is saved. + * - 4: an RGBA image file is saved. + * - 8: an RGBA image and a Z-buffer (non-null `zptr`). */ - -static bool output_iris(uint *lptr, int xsize, int ysize, int zsize, const char *name, int *zptr) +static bool output_iris(const char *filepath, + const uint *lptr, + const int *zptr, + const int xsize, + const int ysize, + const int zsize) { FILE *outf; IMAGE *image; @@ -801,7 +807,7 @@ static bool output_iris(uint *lptr, int xsize, int ysize, int zsize, const char int rlebuflen, goodwrite; goodwrite = 1; - outf = BLI_fopen(name, "wb"); + outf = BLI_fopen(filepath, "wb"); if (!outf) { return 0; } @@ -837,21 +843,20 @@ static bool output_iris(uint *lptr, int xsize, int ysize, int zsize, const char for (z = 0; z < zsize; z++) { if (zsize == 1) { - lumrow((uchar *)lptr, (uchar *)lumbuf, xsize); - len = compressrow((uchar *)lumbuf, rlebuf, CHANOFFSET(z), xsize); + lumrow((const uchar *)lptr, (uchar *)lumbuf, xsize); + len = compressrow((const uchar *)lumbuf, rlebuf, CHANOFFSET(z), xsize); } else { if (z < 4) { - len = compressrow((uchar *)lptr, rlebuf, CHANOFFSET(z), xsize); + len = compressrow((const uchar *)lptr, rlebuf, CHANOFFSET(z), xsize); } else if (z < 8 && zptr) { - len = compressrow((uchar *)zptr, rlebuf, CHANOFFSET(z - 4), xsize); + len = compressrow((const uchar *)zptr, rlebuf, CHANOFFSET(z - 4), xsize); } } - if (len > rlebuflen) { - fprintf(stderr, "output_iris: rlebuf is too small - bad poop\n"); - exit(1); - } + + BLI_assert_msg(len <= rlebuflen, "The length calculated for 'rlebuflen' was too small!"); + goodwrite *= fwrite(rlebuf, len, 1, outf); starttab[y + z * ysize] = pos; lengthtab[y + z * ysize] = len; @@ -892,9 +897,10 @@ static void lumrow(const uchar *rgbptr, uchar *lumptr, int n) } } -static int compressrow(uchar *lbuf, uchar *rlebuf, int z, int row_len) +static int compressrow(const uchar *lbuf, uchar *rlebuf, const int z, const int row_len) { - uchar *iptr, *ibufend, *sptr, *optr; + const uchar *iptr, *ibufend, *sptr; + uchar *optr; short todo, cc; int count; @@ -964,7 +970,7 @@ bool imb_saveiris(struct ImBuf *ibuf, const char *filepath, int flags) IMB_convert_rgba_to_abgr(ibuf); test_endian_zbuf(ibuf); - const bool ok = output_iris(ibuf->rect, ibuf->x, ibuf->y, zsize, filepath, ibuf->zbuf); + const bool ok = output_iris(filepath, ibuf->rect, ibuf->zbuf, ibuf->x, ibuf->y, zsize); /* restore! Quite clumsy, 2 times a switch... maybe better a malloc ? */ IMB_convert_rgba_to_abgr(ibuf); diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c index 6fb1fb52153..cffa61977f7 100644 --- a/source/blender/imbuf/intern/jpeg.c +++ b/source/blender/imbuf/intern/jpeg.c @@ -39,7 +39,11 @@ static void skip_input_data(j_decompress_ptr cinfo, long num_bytes); static void term_source(j_decompress_ptr cinfo); static void memory_source(j_decompress_ptr cinfo, const unsigned char *buffer, size_t size); static boolean handle_app1(j_decompress_ptr cinfo); -static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int flags); +static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, + int flags, + int max_size, + size_t *r_width, + size_t *r_height); static const uchar jpeg_default_quality = 75; static uchar ibuf_quality; @@ -246,7 +250,11 @@ static boolean handle_app1(j_decompress_ptr cinfo) return true; } -static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int flags) +static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, + int flags, + int max_size, + size_t *r_width, + size_t *r_height) { JSAMPARRAY row_pointer; JSAMPLE *buffer = NULL; @@ -264,16 +272,34 @@ static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int fla jpeg_save_markers(cinfo, JPEG_COM, 0xffff); if (jpeg_read_header(cinfo, false) == JPEG_HEADER_OK) { - x = cinfo->image_width; - y = cinfo->image_height; depth = cinfo->num_components; if (cinfo->jpeg_color_space == JCS_YCCK) { cinfo->out_color_space = JCS_CMYK; } + if (r_width) { + *r_width = cinfo->image_width; + } + if (r_height) { + *r_height = cinfo->image_height; + } + + if (max_size > 0) { + /* `libjpeg` can more quickly decompress while scaling down to 1/2, 1/4, 1/8, + * while `libjpeg-turbo` can also do 3/8, 5/8, etc. But max is 1/8. */ + float scale = (float)max_size / MAX2(cinfo->image_width, cinfo->image_height); + cinfo->scale_denom = 8; + cinfo->scale_num = max_uu(1, min_uu(8, ceill(scale * (float)cinfo->scale_denom))); + cinfo->dct_method = JDCT_FASTEST; + cinfo->dither_mode = JDITHER_ORDERED; + } + jpeg_start_decompress(cinfo); + x = cinfo->output_width; + y = cinfo->output_height; + if (flags & IB_test) { jpeg_abort_decompress(cinfo); ibuf = IMB_allocImBuf(x, y, 8 * depth, 0); @@ -449,11 +475,92 @@ ImBuf *imb_load_jpeg(const unsigned char *buffer, jpeg_create_decompress(cinfo); memory_source(cinfo, buffer, size); - ibuf = ibJpegImageFromCinfo(cinfo, flags); + ibuf = ibJpegImageFromCinfo(cinfo, flags, -1, NULL, NULL); + + return ibuf; +} + +/* Defines for JPEG Header markers and segment size. */ +#define JPEG_MARKER_MSB (0xFF) +#define JPEG_MARKER_SOI (0xD8) +#define JPEG_MARKER_APP1 (0xE1) +#define JPEG_APP1_MAX (1 << 16) + +struct ImBuf *imb_thumbnail_jpeg(const char *filepath, + const int flags, + const size_t max_thumb_size, + char colorspace[IM_MAX_SPACE], + size_t *r_width, + size_t *r_height) +{ + struct jpeg_decompress_struct _cinfo, *cinfo = &_cinfo; + struct my_error_mgr jerr; + FILE *infile = NULL; + + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); + + cinfo->err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = jpeg_error; + + /* Establish the setjmp return context for my_error_exit to use. */ + if (setjmp(jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object, close the input file, and return. + */ + jpeg_destroy_decompress(cinfo); + return NULL; + } + + if ((infile = BLI_fopen(filepath, "rb")) == NULL) { + fprintf(stderr, "can't open %s\n", filepath); + return NULL; + } + + /* If file contains an embedded thumbnail, let's return that instead. */ + + if ((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI) && + (fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_APP1)) { + /* This is a JPEG in EXIF format (SOI + APP1), not JFIF (SOI + APP0). */ + unsigned int i = JPEG_APP1_MAX; + /* All EXIF data is within this 64K header segment. Skip ahead until next SOI for thumbnail. */ + while (!((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI)) && + !feof(infile) && i--) + ; + if (i > 0 && !feof(infile)) { + /* We found a JPEG thumbnail inside this image. */ + ImBuf *ibuf = NULL; + uchar *buffer = MEM_callocN(JPEG_APP1_MAX, "thumbbuffer"); + /* Just put SOI directly in buffer rather than seeking back 2 bytes. */ + buffer[0] = JPEG_MARKER_MSB; + buffer[1] = JPEG_MARKER_SOI; + if (fread(buffer + 2, JPEG_APP1_MAX - 2, 1, infile) == 1) { + ibuf = imb_load_jpeg(buffer, JPEG_APP1_MAX, flags, colorspace); + } + MEM_SAFE_FREE(buffer); + if (ibuf) { + fclose(infile); + return ibuf; + } + } + } + + /* No embedded thumbnail found, so let's create a new one. */ + + fseek(infile, 0, SEEK_SET); + jpeg_create_decompress(cinfo); + + jpeg_stdio_src(cinfo, infile); + ImBuf *ibuf = ibJpegImageFromCinfo(cinfo, flags, max_thumb_size, r_width, r_height); + fclose(infile); return ibuf; } +#undef JPEG_MARKER_MSB +#undef JPEG_MARKER_SOI +#undef JPEG_MARKER_APP1 +#undef JPEG_APP1_MAX + static void write_jpeg(struct jpeg_compress_struct *cinfo, struct ImBuf *ibuf) { JSAMPLE *buffer = NULL; diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c index df41c0ca757..4b433836767 100644 --- a/source/blender/imbuf/intern/readimage.c +++ b/source/blender/imbuf/intern/readimage.c @@ -22,6 +22,8 @@ #include "IMB_filetype.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_metadata.h" +#include "IMB_thumbs.h" #include "imbuf.h" #include "IMB_colormanagement.h" @@ -187,21 +189,21 @@ ImBuf *IMB_loadifffile( return ibuf; } -static void imb_cache_filename(char *filename, const char *name, int flags) +static void imb_cache_filename(char *filepath, const char *name, int flags) { /* read .tx instead if it exists and is not older */ if (flags & IB_tilecache) { - BLI_strncpy(filename, name, IMB_FILENAME_SIZE); - if (!BLI_path_extension_replace(filename, IMB_FILENAME_SIZE, ".tx")) { + BLI_strncpy(filepath, name, IMB_FILENAME_SIZE); + if (!BLI_path_extension_replace(filepath, IMB_FILENAME_SIZE, ".tx")) { return; } - if (BLI_file_older(name, filename)) { + if (BLI_file_older(name, filepath)) { return; } } - BLI_strncpy(filename, name, IMB_FILENAME_SIZE); + BLI_strncpy(filepath, name, IMB_FILENAME_SIZE); } ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]) @@ -234,6 +236,61 @@ ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_S return ibuf; } +struct ImBuf *IMB_thumb_load_image(const char *filepath, + size_t max_thumb_size, + char colorspace[IM_MAX_SPACE]) +{ + const ImFileType *type = IMB_file_type_from_ftype(IMB_ispic_type(filepath)); + if (type == NULL) { + return NULL; + } + + ImBuf *ibuf = NULL; + int flags = IB_rect | IB_metadata; + /* Size of the original image. */ + size_t width = 0; + size_t height = 0; + + char effective_colorspace[IM_MAX_SPACE] = ""; + if (colorspace) { + BLI_strncpy(effective_colorspace, colorspace, sizeof(effective_colorspace)); + } + + if (type->load_filepath_thumbnail) { + ibuf = type->load_filepath_thumbnail( + filepath, flags, max_thumb_size, colorspace, &width, &height); + } + else { + /* Skip images of other types if over 100MB. */ + const size_t file_size = BLI_file_size(filepath); + if (file_size != -1 && file_size > THUMB_SIZE_MAX) { + return NULL; + } + ibuf = IMB_loadiffname(filepath, flags, colorspace); + if (ibuf) { + width = ibuf->x; + height = ibuf->y; + } + } + + if (ibuf) { + imb_handle_alpha(ibuf, flags, colorspace, effective_colorspace); + + if (width > 0 && height > 0) { + /* Save dimensions of original image into the thumbnail metadata. */ + char cwidth[40]; + char cheight[40]; + SNPRINTF(cwidth, "%zu", width); + SNPRINTF(cheight, "%zu", height); + IMB_metadata_ensure(&ibuf->metadata); + IMB_metadata_set_field(ibuf->metadata, "Thumb::Image::Width", cwidth); + IMB_metadata_set_field(ibuf->metadata, "Thumb::Image::Height", cheight); + } + } + + return ibuf; +} + ImBuf *IMB_testiffname(const char *filepath, int flags) { ImBuf *ibuf; diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index 37734ebacb2..f2c9c82fa66 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -318,12 +318,8 @@ static ImBuf *thumb_create_ex(const char *file_path, char tpath[FILE_MAX]; char tdir[FILE_MAX]; char temp[FILE_MAX]; - char mtime[40] = "0"; /* in case we can't stat the file */ - char cwidth[40] = "0"; /* in case images have no data */ - char cheight[40] = "0"; + char mtime[40] = "0"; /* in case we can't stat the file */ short tsize = 128; - short ex, ey; - float scaledx, scaledy; BLI_stat_t info; switch (size) { @@ -340,15 +336,6 @@ static ImBuf *thumb_create_ex(const char *file_path, return NULL; /* unknown size */ } - /* exception, skip images over 100mb */ - if (source == THB_SOURCE_IMAGE) { - const size_t file_size = BLI_file_size(file_path); - if (file_size != -1 && file_size > THUMB_SIZE_MAX) { - // printf("file too big: %d, skipping %s\n", (int)size, file_path); - return NULL; - } - } - if (get_thumb_dir(tdir, size)) { BLI_snprintf(tpath, FILE_MAX, "%s%s", tdir, thumb); // thumb[8] = '\0'; /* shorten for tempname, not needed anymore */ @@ -368,7 +355,7 @@ static ImBuf *thumb_create_ex(const char *file_path, if (img == NULL) { switch (source) { case THB_SOURCE_IMAGE: - img = IMB_loadiffname(file_path, IB_rect | IB_metadata, NULL); + img = IMB_thumb_load_image(file_path, tsize, NULL); break; case THB_SOURCE_BLEND: img = IMB_thumb_load_blend(file_path, blen_group, blen_id); @@ -385,8 +372,6 @@ static ImBuf *thumb_create_ex(const char *file_path, if (BLI_stat(file_path, &info) != -1) { BLI_snprintf(mtime, sizeof(mtime), "%ld", (long int)info.st_mtime); } - BLI_snprintf(cwidth, sizeof(cwidth), "%d", img->x); - BLI_snprintf(cheight, sizeof(cheight), "%d", img->y); } } else if (THB_SOURCE_MOVIE == source) { @@ -411,28 +396,20 @@ static ImBuf *thumb_create_ex(const char *file_path, return NULL; } - if (img->x > img->y) { - scaledx = (float)tsize; - scaledy = ((float)img->y / (float)img->x) * tsize; - } - else { - scaledy = (float)tsize; - scaledx = ((float)img->x / (float)img->y) * tsize; - } - /* Scaling down must never assign zero width/height, see: T89868. */ - ex = MAX2(1, (short)scaledx); - ey = MAX2(1, (short)scaledy); - - /* save some time by only scaling byte buf */ - if (img->rect_float) { - if (img->rect == NULL) { - IMB_rect_from_float(img); + if (img->x > tsize || img->y > tsize) { + float scale = MIN2((float)tsize / (float)img->x, (float)tsize / (float)img->y); + /* Scaling down must never assign zero width/height, see: T89868. */ + short ex = MAX2(1, (short)(img->x * scale)); + short ey = MAX2(1, (short)(img->y * scale)); + /* Save some time by only scaling byte buf */ + if (img->rect_float) { + if (img->rect == NULL) { + IMB_rect_from_float(img); + } + imb_freerectfloatImBuf(img); } - - imb_freerectfloatImBuf(img); + IMB_scaleImBuf(img, ex, ey); } - - IMB_scaleImBuf(img, ex, ey); } BLI_snprintf(desc, sizeof(desc), "Thumbnail for %s", uri); IMB_metadata_ensure(&img->metadata); @@ -443,10 +420,6 @@ static ImBuf *thumb_create_ex(const char *file_path, if (use_hash) { IMB_metadata_set_field(img->metadata, "X-Blender::Hash", hash); } - if (ELEM(source, THB_SOURCE_IMAGE, THB_SOURCE_BLEND, THB_SOURCE_FONT)) { - IMB_metadata_set_field(img->metadata, "Thumb::Image::Width", cwidth); - IMB_metadata_set_field(img->metadata, "Thumb::Image::Height", cheight); - } img->ftype = IMB_FTYPE_PNG; img->planes = 32; @@ -493,27 +466,27 @@ static ImBuf *thumb_create_or_fail(const char *file_path, return img; } -ImBuf *IMB_thumb_create(const char *path, ThumbSize size, ThumbSource source, ImBuf *img) +ImBuf *IMB_thumb_create(const char *filepath, ThumbSize size, ThumbSource source, ImBuf *img) { char uri[URI_MAX] = ""; char thumb_name[40]; - if (!uri_from_filename(path, uri)) { + if (!uri_from_filename(filepath, uri)) { return NULL; } thumbname_from_uri(uri, thumb_name, sizeof(thumb_name)); return thumb_create_ex( - path, uri, thumb_name, false, THUMB_DEFAULT_HASH, NULL, NULL, size, source, img); + filepath, uri, thumb_name, false, THUMB_DEFAULT_HASH, NULL, NULL, size, source, img); } -ImBuf *IMB_thumb_read(const char *path, ThumbSize size) +ImBuf *IMB_thumb_read(const char *filepath, ThumbSize size) { char thumb[FILE_MAX]; char uri[URI_MAX]; ImBuf *img = NULL; - if (!uri_from_filename(path, uri)) { + if (!uri_from_filename(filepath, uri)) { return NULL; } if (thumbpath_from_uri(uri, thumb, sizeof(thumb), size)) { @@ -523,16 +496,16 @@ ImBuf *IMB_thumb_read(const char *path, ThumbSize size) return img; } -void IMB_thumb_delete(const char *path, ThumbSize size) +void IMB_thumb_delete(const char *filepath, ThumbSize size) { char thumb[FILE_MAX]; char uri[URI_MAX]; - if (!uri_from_filename(path, uri)) { + if (!uri_from_filename(filepath, uri)) { return; } if (thumbpath_from_uri(uri, thumb, sizeof(thumb), size)) { - if (BLI_path_ncmp(path, thumb, sizeof(thumb)) == 0) { + if (BLI_path_ncmp(filepath, thumb, sizeof(thumb)) == 0) { return; } if (BLI_exists(thumb)) { diff --git a/source/blender/imbuf/intern/thumbs_font.c b/source/blender/imbuf/intern/thumbs_font.c index 7a1c4947c99..c0a33f608a5 100644 --- a/source/blender/imbuf/intern/thumbs_font.c +++ b/source/blender/imbuf/intern/thumbs_font.c @@ -41,7 +41,7 @@ void IMB_thumb_ensure_translations(void) } } -struct ImBuf *IMB_thumb_load_font(const char *filename, unsigned int x, unsigned int y) +struct ImBuf *IMB_thumb_load_font(const char *filepath, unsigned int x, unsigned int y) { const int font_size = y / 4; @@ -60,7 +60,7 @@ struct ImBuf *IMB_thumb_load_font(const char *filename, unsigned int x, unsigned /* draw with full alpha */ font_color[3] = 1.0f; - BLF_thumb_preview(filename, + BLF_thumb_preview(filepath, thumb_str, i18n_thumb_str, ARRAY_SIZE(thumb_str), diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c index 45b50c866fe..ffa989a29b4 100644 --- a/source/blender/imbuf/intern/util.c +++ b/source/blender/imbuf/intern/util.c @@ -390,14 +390,3 @@ bool IMB_isanim(const char *filepath) return (type && type != ANIM_SEQUENCE); } - -bool IMB_isfloat(const ImBuf *ibuf) -{ - const ImFileType *type = IMB_file_type_from_ibuf(ibuf); - if (type != NULL) { - if (type->flag & IM_FTYPE_FLOAT) { - return true; - } - } - return false; -} diff --git a/source/blender/imbuf/intern/writeimage.c b/source/blender/imbuf/intern/writeimage.c index 56c9384a330..d2c0b61c1c5 100644 --- a/source/blender/imbuf/intern/writeimage.c +++ b/source/blender/imbuf/intern/writeimage.c @@ -19,11 +19,6 @@ #include "IMB_colormanagement.h" #include "IMB_colormanagement_intern.h" -static bool prepare_write_imbuf(const ImFileType *type, ImBuf *ibuf) -{ - return IMB_prepare_write_ImBuf((type->flag & IM_FTYPE_FLOAT), ibuf); -} - bool IMB_saveiff(struct ImBuf *ibuf, const char *filepath, int flags) { errno = 0; @@ -36,34 +31,22 @@ bool IMB_saveiff(struct ImBuf *ibuf, const char *filepath, int flags) ibuf->flags = flags; const ImFileType *type = IMB_file_type_from_ibuf(ibuf); - if (type != NULL) { - if (type->save != NULL) { - prepare_write_imbuf(type, ibuf); - return type->save(ibuf, filepath, flags); - } + if (type == NULL || type->save == NULL) { + fprintf(stderr, "Couldn't save picture.\n"); + return false; } - fprintf(stderr, "Couldn't save picture.\n"); - - return false; -} - -bool IMB_prepare_write_ImBuf(const bool isfloat, ImBuf *ibuf) -{ - bool changed = false; - - if (isfloat) { - /* pass */ - } - else { + /* If writing byte image from float buffer, create a byte buffer for writing. + * + * For color managed image writing, IMB_colormanagement_imbuf_for_write should + * have already created this byte buffer. This is a basic fallback for other + * cases where we do not have a specific desired output colorspace. */ + if (!(type->flag & IM_FTYPE_FLOAT)) { if (ibuf->rect == NULL && ibuf->rect_float) { ibuf->rect_colorspace = colormanage_colorspace_get_roled(COLOR_ROLE_DEFAULT_BYTE); IMB_rect_from_float(ibuf); - if (ibuf->rect != NULL) { - changed = true; - } } } - return changed; + return type->save(ibuf, filepath, flags); } diff --git a/source/blender/io/alembic/exporter/abc_export_capi.cc b/source/blender/io/alembic/exporter/abc_export_capi.cc index c1ff7df0c37..edaf53b3efa 100644 --- a/source/blender/io/alembic/exporter/abc_export_capi.cc +++ b/source/blender/io/alembic/exporter/abc_export_capi.cc @@ -115,7 +115,7 @@ static void export_startjob(void *customdata, return; } - ABCHierarchyIterator iter(data->depsgraph, abc_archive.get(), data->params); + ABCHierarchyIterator iter(data->bmain, data->depsgraph, abc_archive.get(), data->params); if (export_animation) { CLOG_INFO(&LOG, 2, "Exporting animation"); diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc index d33adfdb4b9..ab62694b802 100644 --- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc +++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc @@ -26,10 +26,11 @@ namespace blender::io::alembic { -ABCHierarchyIterator::ABCHierarchyIterator(Depsgraph *depsgraph, +ABCHierarchyIterator::ABCHierarchyIterator(Main *bmain, + Depsgraph *depsgraph, ABCArchive *abc_archive, const AlembicExportParams ¶ms) - : AbstractHierarchyIterator(depsgraph), abc_archive_(abc_archive), params_(params) + : AbstractHierarchyIterator(bmain, depsgraph), abc_archive_(abc_archive), params_(params) { } diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h index 24d3f319fbd..f7814c28a55 100644 --- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h +++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h @@ -13,6 +13,7 @@ #include <Alembic/Abc/OObject.h> struct Depsgraph; +struct Main; struct Object; namespace blender::io::alembic { @@ -36,7 +37,8 @@ class ABCHierarchyIterator : public AbstractHierarchyIterator { const AlembicExportParams ¶ms_; public: - ABCHierarchyIterator(Depsgraph *depsgraph, + ABCHierarchyIterator(Main *bmain, + Depsgraph *depsgraph, ABCArchive *abc_archive_, const AlembicExportParams ¶ms); diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc index 45bf898f3f5..633611cf1a6 100644 --- a/source/blender/io/alembic/intern/abc_customdata.cc +++ b/source/blender/io/alembic/intern/abc_customdata.cc @@ -48,9 +48,9 @@ static const std::string propNameOriginalCoordinates("Pref"); static void get_uvs(const CDStreamConfig &config, std::vector<Imath::V2f> &uvs, std::vector<uint32_t> &uvidx, - void *cd_data) + const void *cd_data) { - MLoopUV *mloopuv_array = static_cast<MLoopUV *>(cd_data); + const MLoopUV *mloopuv_array = static_cast<const MLoopUV *>(cd_data); if (!mloopuv_array) { return; @@ -68,7 +68,7 @@ static void get_uvs(const CDStreamConfig &config, /* Iterate in reverse order to match exported polygons. */ for (int i = 0; i < num_poly; i++) { MPoly ¤t_poly = polygons[i]; - MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop; + const MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop; for (int j = 0; j < current_poly.totloop; j++, count++) { loopuv--; @@ -87,7 +87,7 @@ static void get_uvs(const CDStreamConfig &config, for (int i = 0; i < num_poly; i++) { MPoly ¤t_poly = polygons[i]; MLoop *looppoly = mloop + current_poly.loopstart + current_poly.totloop; - MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop; + const MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop; for (int j = 0; j < current_poly.totloop; j++) { looppoly--; @@ -125,7 +125,7 @@ const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, Custom return ""; } - void *cd_data = CustomData_get_layer_n(data, CD_MLOOPUV, active_uvlayer); + const void *cd_data = CustomData_get_layer_n(data, CD_MLOOPUV, active_uvlayer); get_uvs(config, sample.uvs, sample.indices, cd_data); @@ -139,7 +139,7 @@ const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, Custom */ static void write_uv(const OCompoundProperty &prop, CDStreamConfig &config, - void *data, + const void *data, const char *name) { std::vector<uint32_t> indices; @@ -169,12 +169,12 @@ static void write_uv(const OCompoundProperty &prop, static void get_cols(const CDStreamConfig &config, std::vector<Imath::C4f> &buffer, std::vector<uint32_t> &uvidx, - void *cd_data) + const void *cd_data) { const float cscale = 1.0f / 255.0f; - MPoly *polys = config.mpoly; - MLoop *mloops = config.mloop; - MCol *cfaces = static_cast<MCol *>(cd_data); + const MPoly *polys = config.mpoly; + const MLoop *mloops = config.mloop; + const MCol *cfaces = static_cast<const MCol *>(cd_data); buffer.reserve(config.totvert); uvidx.reserve(config.totvert); @@ -182,9 +182,9 @@ static void get_cols(const CDStreamConfig &config, Imath::C4f col; for (int i = 0; i < config.totpoly; i++) { - MPoly *p = &polys[i]; - MCol *cface = &cfaces[p->loopstart + p->totloop]; - MLoop *mloop = &mloops[p->loopstart + p->totloop]; + const MPoly *p = &polys[i]; + const MCol *cface = &cfaces[p->loopstart + p->totloop]; + const MLoop *mloop = &mloops[p->loopstart + p->totloop]; for (int j = 0; j < p->totloop; j++) { cface--; @@ -207,7 +207,7 @@ static void get_cols(const CDStreamConfig &config, */ static void write_mcol(const OCompoundProperty &prop, CDStreamConfig &config, - void *data, + const void *data, const char *name) { std::vector<uint32_t> indices; @@ -283,7 +283,7 @@ void write_custom_data(const OCompoundProperty &prop, const int tot_layers = CustomData_number_of_layers(data, cd_data_type); for (int i = 0; i < tot_layers; i++) { - void *cd_data = CustomData_get_layer_n(data, cd_data_type, i); + const void *cd_data = CustomData_get_layer_n(data, cd_data_type, i); const char *name = CustomData_get_layer_name(data, cd_data_type, i); if (cd_data_type == CD_MLOOPUV) { diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index 8c62484028d..219bba285a7 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -377,25 +377,22 @@ BLI_INLINE void read_uvs_params(CDStreamConfig &config, static void *add_customdata_cb(Mesh *mesh, const char *name, int data_type) { CustomDataType cd_data_type = static_cast<CustomDataType>(data_type); - void *cd_ptr; - CustomData *loopdata; - int numloops; /* unsupported custom data type -- don't do anything. */ if (!ELEM(cd_data_type, CD_MLOOPUV, CD_PROP_BYTE_COLOR)) { return nullptr; } - loopdata = &mesh->ldata; - cd_ptr = CustomData_get_layer_named(loopdata, cd_data_type, name); + void *cd_ptr = CustomData_get_layer_named(&mesh->ldata, cd_data_type, name); if (cd_ptr != nullptr) { /* layer already exists, so just return it. */ return cd_ptr; } /* Create a new layer. */ - numloops = mesh->totloop; - cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT, nullptr, numloops, name); + int numloops = mesh->totloop; + cd_ptr = CustomData_add_layer_named( + &mesh->ldata, cd_data_type, CD_DEFAULT, nullptr, numloops, name); return cd_ptr; } diff --git a/source/blender/io/collada/AnimationImporter.h b/source/blender/io/collada/AnimationImporter.h index 7c5b38f5f8d..1c5b7570b9d 100644 --- a/source/blender/io/collada/AnimationImporter.h +++ b/source/blender/io/collada/AnimationImporter.h @@ -228,7 +228,7 @@ class AnimationImporter : private TransformReader, public AnimationImporterBase COLLADAFW::Transformation::TransformationType tm_type); /** * Internal, better make it private - * warning: evaluates only rotation and only assigns matrix transforms now + * WARNING: evaluates only rotation and only assigns matrix transforms now * prerequisites: animlist_map, curve_map. */ void evaluate_transform_at_frame(float mat[4][4], COLLADAFW::Node *node, float fra); diff --git a/source/blender/io/collada/GeometryExporter.cpp b/source/blender/io/collada/GeometryExporter.cpp index 3952a4ae334..7e2a24aeb41 100644 --- a/source/blender/io/collada/GeometryExporter.cpp +++ b/source/blender/io/collada/GeometryExporter.cpp @@ -477,7 +477,8 @@ void GeometryExporter::createVertexColorSource(std::string geom_id, Mesh *me) for (int a = 0; a < totlayer_mcol; a++) { map_index++; - MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(&me->ldata, CD_PROP_BYTE_COLOR, a); + const MLoopCol *mloopcol = (const MLoopCol *)CustomData_get_layer_n( + &me->ldata, CD_PROP_BYTE_COLOR, a); COLLADASW::FloatSourceF source(mSW); @@ -502,7 +503,7 @@ void GeometryExporter::createVertexColorSource(std::string geom_id, Mesh *me) MPoly *mpoly; int i; for (i = 0, mpoly = me->mpoly; i < me->totpoly; i++, mpoly++) { - MLoopCol *mlc = mloopcol + mpoly->loopstart; + const MLoopCol *mlc = mloopcol + mpoly->loopstart; for (int j = 0; j < mpoly->totloop; j++, mlc++) { source.appendValues(mlc->r / 255.0f, mlc->g / 255.0f, mlc->b / 255.0f, mlc->a / 255.0f); } @@ -619,7 +620,7 @@ void GeometryExporter::create_normals(std::vector<Normal> &normals, MVert *verts = me->mvert; const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me); MLoop *mloops = me->mloop; - float(*lnors)[3] = nullptr; + const float(*lnors)[3] = nullptr; bool use_custom_normals = false; BKE_mesh_calc_normals_split(me); diff --git a/source/blender/io/collada/MeshImporter.h b/source/blender/io/collada/MeshImporter.h index 79b9778738d..85f960abfe6 100644 --- a/source/blender/io/collada/MeshImporter.h +++ b/source/blender/io/collada/MeshImporter.h @@ -142,7 +142,7 @@ class MeshImporter : public MeshImporterBase { /** * Return the number of faces by summing up * the face-counts of the parts. - * hint: This is done because `mesh->getFacesCount()` does + * HINT: This is done because `mesh->getFacesCount()` does * count loose edges as extra faces, which is not what we want here. */ void allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me); @@ -150,7 +150,7 @@ class MeshImporter : public MeshImporterBase { /* TODO: import uv set names */ /** * Read all faces from TRIANGLES, TRIANGLE_FANS, POLYLIST, POLYGON - * Important: This function MUST be called before read_lines() + * IMPORTANT: This function MUST be called before read_lines() * Otherwise we will lose all edges from faces (see read_lines() above) * * TODO: import uv set names. @@ -158,7 +158,7 @@ class MeshImporter : public MeshImporterBase { void read_polys(COLLADAFW::Mesh *mesh, Mesh *me); /** * Read all loose edges. - * Important: This function assumes that all edges from existing + * IMPORTANT: This function assumes that all edges from existing * faces have already been generated and added to me->medge * So this function MUST be called after read_faces() (see below) */ diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt index b5766b44025..e80bd850825 100644 --- a/source/blender/io/common/CMakeLists.txt +++ b/source/blender/io/common/CMakeLists.txt @@ -8,7 +8,6 @@ set(INC ../../depsgraph ../../makesdna ../../../../intern/guardedalloc - ../../../../extern/fast_float ) set(INC_SYS @@ -20,13 +19,11 @@ set(SRC intern/dupli_persistent_id.cc intern/object_identifier.cc intern/path_util.cc - intern/string_utils.cc IO_abstract_hierarchy_iterator.h IO_dupli_persistent_id.hh IO_path_util.hh IO_path_util_types.h - IO_string_utils.hh IO_types.h intern/dupli_parent_finder.hh ) @@ -45,7 +42,6 @@ if(WITH_GTESTS) intern/abstract_hierarchy_iterator_test.cc intern/hierarchy_context_order_test.cc intern/object_identifier_test.cc - intern/string_utils_test.cc ) set(TEST_INC ../../blenloader diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h index 3c6b6cc631e..3371501db95 100644 --- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h +++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h @@ -30,6 +30,7 @@ struct Depsgraph; struct DupliObject; struct ID; +struct Main; struct Object; struct ParticleSystem; @@ -204,12 +205,13 @@ class AbstractHierarchyIterator { protected: ExportGraph export_graph_; ExportPathMap duplisource_export_path_; + Main *bmain_; Depsgraph *depsgraph_; WriterMap writers_; ExportSubset export_subset_; public: - explicit AbstractHierarchyIterator(Depsgraph *depsgraph); + explicit AbstractHierarchyIterator(Main *bmain, Depsgraph *depsgraph); virtual ~AbstractHierarchyIterator(); /* Iterate over the depsgraph, create writers, and tell the writers to write. diff --git a/source/blender/io/common/IO_string_utils.hh b/source/blender/io/common/IO_string_utils.hh deleted file mode 100644 index 25f1f01c6ed..00000000000 --- a/source/blender/io/common/IO_string_utils.hh +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#pragma once - -#include "BLI_string_ref.hh" - -/* - * Various text parsing utilities commonly used by text-based input formats. - */ - -namespace blender::io { - -/** - * Fetches next line from an input string buffer. - * - * The returned line will not have '\n' characters at the end; - * the `buffer` is modified to contain remaining text without - * the input line. - * - * Note that backslash (\) character is treated as a line - * continuation, similar to OBJ file format or a C preprocessor. - */ -StringRef read_next_line(StringRef &buffer); - -/** - * Drop leading white-space from a StringRef. - * Note that backslash character is considered white-space. - */ -StringRef drop_whitespace(StringRef str); - -/** - * Drop leading non-white-space from a StringRef. - * Note that backslash character is considered white-space. - */ -StringRef drop_non_whitespace(StringRef str); - -/** - * Parse an integer from an input string. - * The parsed result is stored in `dst`. The function skips - * leading white-space unless `skip_space=false`. If the - * number can't be parsed (invalid syntax, out of range), - * `fallback` value is stored instead. - * - * Returns the remainder of the input string after parsing. - */ -StringRef parse_int(StringRef str, int fallback, int &dst, bool skip_space = true); - -/** - * Parse a float from an input string. - * The parsed result is stored in `dst`. The function skips - * leading white-space unless `skip_space=false`. If the - * number can't be parsed (invalid syntax, out of range), - * `fallback` value is stored instead. - * - * Returns the remainder of the input string after parsing. - */ -StringRef parse_float(StringRef str, float fallback, float &dst, bool skip_space = true); - -/** - * Parse a number of white-space separated floats from an input string. - * The parsed `count` numbers are stored in `dst`. If a - * number can't be parsed (invalid syntax, out of range), - * `fallback` value is stored instead. - * - * Returns the remainder of the input string after parsing. - */ -StringRef parse_floats(StringRef str, float fallback, float *dst, int count); - -} // namespace blender::io diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc index f0501d4cf62..82bb1c57833 100644 --- a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc @@ -161,8 +161,8 @@ bool AbstractHierarchyWriter::check_has_deforming_physics(const HierarchyContext return rbo != nullptr && rbo->type == RBO_TYPE_ACTIVE && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0; } -AbstractHierarchyIterator::AbstractHierarchyIterator(Depsgraph *depsgraph) - : depsgraph_(depsgraph), export_subset_({true, true}) +AbstractHierarchyIterator::AbstractHierarchyIterator(Main *bmain, Depsgraph *depsgraph) + : bmain_(bmain), depsgraph_(depsgraph), export_subset_({true, true}) { } diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc index 52cae89c2e8..81a3546259e 100644 --- a/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc @@ -54,7 +54,8 @@ class TestingHierarchyIterator : public AbstractHierarchyIterator { used_writers hair_writers; used_writers particle_writers; - explicit TestingHierarchyIterator(Depsgraph *depsgraph) : AbstractHierarchyIterator(depsgraph) + explicit TestingHierarchyIterator(Main *bmain, Depsgraph *depsgraph) + : AbstractHierarchyIterator(bmain, depsgraph) { } ~TestingHierarchyIterator() override @@ -105,7 +106,7 @@ class AbstractHierarchyIteratorTest : public BlendfileLoadingBaseTest { /* Create a test iterator. */ void iterator_create() { - iterator = new TestingHierarchyIterator(depsgraph); + iterator = new TestingHierarchyIterator(bfile->main, depsgraph); } /* Free the test iterator if it is not nullptr. */ void iterator_free() diff --git a/source/blender/io/common/intern/string_utils.cc b/source/blender/io/common/intern/string_utils.cc deleted file mode 100644 index 3a12250e14b..00000000000 --- a/source/blender/io/common/intern/string_utils.cc +++ /dev/null @@ -1,99 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "IO_string_utils.hh" - -/* Note: we could use C++17 <charconv> from_chars to parse - * floats, but even if some compilers claim full support, - * their standard libraries are not quite there yet. - * LLVM/libc++ only has a float parser since LLVM 14, - * and gcc/libstdc++ since 11.1. So until at least these are - * the minimum spec, use an external library. */ -#include "fast_float.h" -#include <charconv> - -namespace blender::io { - -StringRef read_next_line(StringRef &buffer) -{ - const char *start = buffer.begin(); - const char *end = buffer.end(); - size_t len = 0; - char prev = 0; - const char *ptr = start; - while (ptr < end) { - char c = *ptr++; - if (c == '\n' && prev != '\\') { - break; - } - prev = c; - ++len; - } - - buffer = StringRef(ptr, end); - return StringRef(start, len); -} - -static bool is_whitespace(char c) -{ - return c <= ' ' || c == '\\'; -} - -StringRef drop_whitespace(StringRef str) -{ - while (!str.is_empty() && is_whitespace(str[0])) { - str = str.drop_prefix(1); - } - return str; -} - -StringRef drop_non_whitespace(StringRef str) -{ - while (!str.is_empty() && !is_whitespace(str[0])) { - str = str.drop_prefix(1); - } - return str; -} - -static StringRef drop_plus(StringRef str) -{ - if (!str.is_empty() && str[0] == '+') { - str = str.drop_prefix(1); - } - return str; -} - -StringRef parse_float(StringRef str, float fallback, float &dst, bool skip_space) -{ - if (skip_space) { - str = drop_whitespace(str); - } - str = drop_plus(str); - fast_float::from_chars_result res = fast_float::from_chars(str.begin(), str.end(), dst); - if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) { - dst = fallback; - } - return StringRef(res.ptr, str.end()); -} - -StringRef parse_floats(StringRef str, float fallback, float *dst, int count) -{ - for (int i = 0; i < count; ++i) { - str = parse_float(str, fallback, dst[i]); - } - return str; -} - -StringRef parse_int(StringRef str, int fallback, int &dst, bool skip_space) -{ - if (skip_space) { - str = drop_whitespace(str); - } - str = drop_plus(str); - std::from_chars_result res = std::from_chars(str.begin(), str.end(), dst); - if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) { - dst = fallback; - } - return StringRef(res.ptr, str.end()); -} - -} // namespace blender::io diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt index e2e959814fa..7a7c95b29f9 100644 --- a/source/blender/io/usd/CMakeLists.txt +++ b/source/blender/io/usd/CMakeLists.txt @@ -69,6 +69,7 @@ set(SRC intern/usd_writer_mesh.cc intern/usd_writer_metaball.cc intern/usd_writer_transform.cc + intern/usd_writer_volume.cc intern/usd_reader_camera.cc intern/usd_reader_curve.cc @@ -95,6 +96,7 @@ set(SRC intern/usd_writer_mesh.h intern/usd_writer_metaball.h intern/usd_writer_transform.h + intern/usd_writer_volume.h intern/usd_reader_camera.h intern/usd_reader_curve.h @@ -119,8 +121,15 @@ list(APPEND LIB ${BOOST_LIBRARIES} ) -list(APPEND LIB -) +if(WITH_OPENVDB) + add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS}) + list(APPEND INC_SYS + ${OPENVDB_INCLUDE_DIRS} + ) + list(APPEND LIB + ${OPENVDB_LIBRARIES} + ) +endif() blender_add_lib(bf_usd "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/io/usd/intern/usd_capi_export.cc b/source/blender/io/usd/intern/usd_capi_export.cc index d7c1a5adc7c..2049c631671 100644 --- a/source/blender/io/usd/intern/usd_capi_export.cc +++ b/source/blender/io/usd/intern/usd_capi_export.cc @@ -38,7 +38,7 @@ struct ExportJobData { Depsgraph *depsgraph; wmWindowManager *wm; - char filename[FILE_MAX]; + char filepath[FILE_MAX]; USDExportParams params; bool export_ok; @@ -74,13 +74,13 @@ static void export_startjob(void *customdata, /* For restoring the current frame after exporting animation is done. */ const int orig_frame = CFRA; - pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(data->filename); + pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(data->filepath); if (!usd_stage) { /* This happens when the USD JSON files cannot be found. When that happens, * the USD library doesn't know it has the functionality to write USDA and * USDC files, and creating a new UsdStage fails. */ WM_reportf( - RPT_ERROR, "USD Export: unable to find suitable USD plugin to write %s", data->filename); + RPT_ERROR, "USD Export: unable to find suitable USD plugin to write %s", data->filepath); return; } @@ -97,7 +97,7 @@ static void export_startjob(void *customdata, usd_stage->SetEndTimeCode(scene->r.efra); } - USDHierarchyIterator iter(data->depsgraph, usd_stage, data->params); + USDHierarchyIterator iter(data->bmain, data->depsgraph, usd_stage, data->params); if (data->params.export_animation) { /* Writing the animated frames is not 100% of the work, but it's our best guess. */ @@ -145,8 +145,8 @@ static void export_endjob(void *customdata) DEG_graph_free(data->depsgraph); - if (!data->export_ok && BLI_exists(data->filename)) { - BLI_delete(data->filename, false, false); + if (!data->export_ok && BLI_exists(data->filepath)) { + BLI_delete(data->filepath, false, false); } G.is_rendering = false; @@ -171,7 +171,7 @@ bool USD_export(bContext *C, job->bmain = CTX_data_main(C); job->wm = CTX_wm_manager(C); job->export_ok = false; - BLI_strncpy(job->filename, filepath, sizeof(job->filename)); + BLI_strncpy(job->filepath, filepath, sizeof(job->filepath)); job->depsgraph = DEG_graph_new(job->bmain, scene, view_layer, params->evaluation_mode); job->params = *params; diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc index 58cd7d5014e..29b256125f0 100644 --- a/source/blender/io/usd/intern/usd_capi_import.cc +++ b/source/blender/io/usd/intern/usd_capi_import.cc @@ -119,7 +119,7 @@ struct ImportJobData { ViewLayer *view_layer; wmWindowManager *wm; - char filename[1024]; + char filepath[1024]; USDImportParams params; ImportSettings settings; @@ -150,7 +150,7 @@ static void import_startjob(void *customdata, short *stop, short *do_update, flo if (data->params.create_collection) { char display_name[1024]; BLI_path_to_display_name( - display_name, strlen(data->filename), BLI_path_basename(data->filename)); + display_name, strlen(data->filepath), BLI_path_basename(data->filepath)); Collection *import_collection = BKE_collection_add( data->bmain, data->scene->master_collection, display_name); id_fake_user_set(&import_collection->id); @@ -164,10 +164,10 @@ static void import_startjob(void *customdata, short *stop, short *do_update, flo data->view_layer, import_collection); } - BLI_path_abs(data->filename, BKE_main_blendfile_path_from_global()); + BLI_path_abs(data->filepath, BKE_main_blendfile_path_from_global()); CacheFile *cache_file = static_cast<CacheFile *>( - BKE_cachefile_add(data->bmain, BLI_path_basename(data->filename))); + BKE_cachefile_add(data->bmain, BLI_path_basename(data->filepath))); /* Decrement the ID ref-count because it is going to be incremented for each * modifier and constraint that it will be attached to, so since currently @@ -176,7 +176,7 @@ static void import_startjob(void *customdata, short *stop, short *do_update, flo cache_file->is_sequence = data->params.is_sequence; cache_file->scale = data->params.scale; - STRNCPY(cache_file->filepath, data->filename); + STRNCPY(cache_file->filepath, data->filepath); data->settings.cache_file = cache_file; @@ -191,10 +191,10 @@ static void import_startjob(void *customdata, short *stop, short *do_update, flo *data->do_update = true; *data->progress = 0.1f; - pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(data->filename); + pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(data->filepath); if (!stage) { - WM_reportf(RPT_ERROR, "USD Import: unable to open stage to read %s", data->filename); + WM_reportf(RPT_ERROR, "USD Import: unable to open stage to read %s", data->filepath); data->import_ok = false; return; } @@ -356,7 +356,7 @@ bool USD_import(struct bContext *C, job->view_layer = CTX_data_view_layer(C); job->wm = CTX_wm_manager(C); job->import_ok = false; - BLI_strncpy(job->filename, filepath, 1024); + BLI_strncpy(job->filepath, filepath, 1024); job->settings.scale = params->scale; job->settings.sequence_offset = params->offset; @@ -499,13 +499,13 @@ void USD_CacheReader_free(CacheReader *reader) } CacheArchiveHandle *USD_create_handle(struct Main * /*bmain*/, - const char *filename, + const char *filepath, ListBase *object_paths) { /* Must call this so that USD file format plugins are loaded. */ ensure_usd_plugin_path_registered(); - pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(filename); + pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(filepath); if (!stage) { return nullptr; diff --git a/source/blender/io/usd/intern/usd_exporter_context.h b/source/blender/io/usd/intern/usd_exporter_context.h index 5a3f94d01f8..a636d849296 100644 --- a/source/blender/io/usd/intern/usd_exporter_context.h +++ b/source/blender/io/usd/intern/usd_exporter_context.h @@ -8,12 +8,14 @@ #include <pxr/usd/usd/common.h> struct Depsgraph; +struct Main; namespace blender::io::usd { class USDHierarchyIterator; struct USDExporterContext { + Main *bmain; Depsgraph *depsgraph; const pxr::UsdStageRefPtr stage; const pxr::SdfPath usd_path; diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc index 7a0d896fb3e..51261c4d91e 100644 --- a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc @@ -10,6 +10,7 @@ #include "usd_writer_mesh.h" #include "usd_writer_metaball.h" #include "usd_writer_transform.h" +#include "usd_writer_volume.h" #include <string> @@ -28,10 +29,11 @@ namespace blender::io::usd { -USDHierarchyIterator::USDHierarchyIterator(Depsgraph *depsgraph, +USDHierarchyIterator::USDHierarchyIterator(Main *bmain, + Depsgraph *depsgraph, pxr::UsdStageRefPtr stage, const USDExportParams ¶ms) - : AbstractHierarchyIterator(depsgraph), stage_(stage), params_(params) + : AbstractHierarchyIterator(bmain, depsgraph), stage_(stage), params_(params) { } @@ -59,6 +61,15 @@ void USDHierarchyIterator::set_export_frame(float frame_nr) export_time_ = pxr::UsdTimeCode(frame_nr); } +std::string USDHierarchyIterator::get_export_file_path() const +{ + /* Returns the same path that was passed to `stage_` object during it's creation (via + * `pxr::UsdStage::CreateNew` function). */ + const pxr::SdfLayerHandle root_layer = stage_->GetRootLayer(); + const std::string usd_export_file_path = root_layer->GetRealPath(); + return usd_export_file_path; +} + const pxr::UsdTimeCode &USDHierarchyIterator::get_export_time_code() const { return export_time_; @@ -66,7 +77,8 @@ const pxr::UsdTimeCode &USDHierarchyIterator::get_export_time_code() const USDExporterContext USDHierarchyIterator::create_usd_export_context(const HierarchyContext *context) { - return USDExporterContext{depsgraph_, stage_, pxr::SdfPath(context->export_path), this, params_}; + return USDExporterContext{ + bmain_, depsgraph_, stage_, pxr::SdfPath(context->export_path), this, params_}; } AbstractHierarchyWriter *USDHierarchyIterator::create_transform_writer( @@ -93,6 +105,9 @@ AbstractHierarchyWriter *USDHierarchyIterator::create_data_writer(const Hierarch case OB_MBALL: data_writer = new USDMetaballWriter(usd_export_context); break; + case OB_VOLUME: + data_writer = new USDVolumeWriter(usd_export_context); + break; case OB_EMPTY: case OB_CURVES_LEGACY: diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.h b/source/blender/io/usd/intern/usd_hierarchy_iterator.h index d5566103af4..445c37238be 100644 --- a/source/blender/io/usd/intern/usd_hierarchy_iterator.h +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.h @@ -12,6 +12,7 @@ #include <pxr/usd/usd/timeCode.h> struct Depsgraph; +struct Main; struct Object; namespace blender::io::usd { @@ -27,11 +28,13 @@ class USDHierarchyIterator : public AbstractHierarchyIterator { const USDExportParams ¶ms_; public: - USDHierarchyIterator(Depsgraph *depsgraph, + USDHierarchyIterator(Main *bmain, + Depsgraph *depsgraph, pxr::UsdStageRefPtr stage, const USDExportParams ¶ms); void set_export_frame(float frame_nr); + std::string get_export_file_path() const; const pxr::UsdTimeCode &get_export_time_code() const; virtual std::string make_valid_name(const std::string &name) const override; diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc index dbeb9df1ee8..2be9b1c065a 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.cc +++ b/source/blender/io/usd/intern/usd_writer_abstract.cc @@ -47,6 +47,11 @@ bool USDAbstractWriter::is_supported(const HierarchyContext * /*context*/) const return true; } +std::string USDAbstractWriter::get_export_file_path() const +{ + return usd_export_context_.hierarchy_iterator->get_export_file_path(); +} + pxr::UsdTimeCode USDAbstractWriter::get_export_time_code() const { if (is_animated_) { diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h index 66feb6e1070..8adc054c2c2 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.h +++ b/source/blender/io/usd/intern/usd_writer_abstract.h @@ -51,6 +51,7 @@ class USDAbstractWriter : public AbstractHierarchyWriter { protected: virtual void do_write(HierarchyContext &context) = 0; + std::string get_export_file_path() const; pxr::UsdTimeCode get_export_time_code() const; pxr::UsdShadeMaterial ensure_usd_material(const HierarchyContext &context, Material *material); diff --git a/source/blender/io/usd/intern/usd_writer_material.cc b/source/blender/io/usd/intern/usd_writer_material.cc index 1bfc0e50f69..a24877a20bd 100644 --- a/source/blender/io/usd/intern/usd_writer_material.cc +++ b/source/blender/io/usd/intern/usd_writer_material.cc @@ -576,7 +576,7 @@ static std::string get_tex_image_asset_path(bNode *node, char file_path[FILE_MAX]; BLI_split_file_part(path.c_str(), file_path, FILE_MAX); - if (export_params.relative_texture_paths) { + if (export_params.relative_paths) { BLI_path_join(exp_path, FILE_MAX, ".", "textures", file_path, nullptr); } else { @@ -594,7 +594,7 @@ static std::string get_tex_image_asset_path(bNode *node, return exp_path; } - if (export_params.relative_texture_paths) { + if (export_params.relative_paths) { /* Get the path relative to the USD. */ pxr::SdfLayerHandle layer = stage->GetRootLayer(); std::string stage_path = layer->GetRealPath(); @@ -606,11 +606,7 @@ static std::string get_tex_image_asset_path(bNode *node, strcpy(rel_path, path.c_str()); BLI_path_rel(rel_path, stage_path.c_str()); - - /* BLI_path_rel adds '//' as a prefix to the path, if - * generating the relative path was successful. */ - if (rel_path[0] != '/' || rel_path[1] != '/') { - /* No relative path generated. */ + if (!BLI_path_is_rel(rel_path)) { return path; } diff --git a/source/blender/io/usd/intern/usd_writer_volume.cc b/source/blender/io/usd/intern/usd_writer_volume.cc new file mode 100644 index 00000000000..4126be6966a --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_volume.cc @@ -0,0 +1,188 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "usd_writer_volume.h" + +#include <pxr/base/tf/pathUtils.h> +#include <pxr/usd/usdVol/openVDBAsset.h> +#include <pxr/usd/usdVol/volume.h> + +#include "DNA_volume_types.h" +#include "DNA_windowmanager_types.h" + +#include "BKE_volume.h" + +#include "BLI_fileops.h" +#include "BLI_index_range.hh" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "WM_api.h" + +#include "usd_hierarchy_iterator.h" + +namespace blender::io::usd { + +USDVolumeWriter::USDVolumeWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) +{ +} + +bool USDVolumeWriter::check_is_animated(const HierarchyContext &context) const +{ + const Volume *volume = static_cast<Volume *>(context.object->data); + return volume->is_sequence; +} + +void USDVolumeWriter::do_write(HierarchyContext &context) +{ + Volume *volume = static_cast<Volume *>(context.object->data); + if (!BKE_volume_load(volume, usd_export_context_.bmain)) { + return; + } + + const int num_grids = BKE_volume_num_grids(volume); + if (!num_grids) { + return; + } + + auto vdb_file_path = resolve_vdb_file(volume); + if (!vdb_file_path.has_value()) { + WM_reportf(RPT_WARNING, + "USD Export: failed to resolve .vdb file for object: %s", + volume->id.name + 2); + return; + } + + if (usd_export_context_.export_params.relative_paths) { + if (auto relative_vdb_file_path = construct_vdb_relative_file_path(*vdb_file_path)) { + vdb_file_path = relative_vdb_file_path; + } + else { + WM_reportf(RPT_WARNING, + "USD Export: couldn't construct relative file path for .vdb file, absolute path " + "will be used instead"); + } + } + + const pxr::UsdTimeCode timecode = get_export_time_code(); + const pxr::SdfPath &volume_path = usd_export_context_.usd_path; + pxr::UsdStageRefPtr stage = usd_export_context_.stage; + pxr::UsdVolVolume usd_volume = pxr::UsdVolVolume::Define(stage, volume_path); + + for (const int i : IndexRange(num_grids)) { + const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i); + const std::string grid_name = BKE_volume_grid_name(grid); + const std::string grid_id = pxr::TfMakeValidIdentifier(grid_name); + const pxr::SdfPath grid_path = volume_path.AppendPath(pxr::SdfPath(grid_id)); + pxr::UsdVolOpenVDBAsset usd_grid = pxr::UsdVolOpenVDBAsset::Define(stage, grid_path); + usd_grid.GetFieldNameAttr().Set(pxr::TfToken(grid_name), timecode); + usd_grid.GetFilePathAttr().Set(pxr::SdfAssetPath(*vdb_file_path), timecode); + usd_volume.CreateFieldRelationship(pxr::TfToken(grid_id), grid_path); + } + + float3 volume_bound_min(std::numeric_limits<float>::max()); + float3 volume_bound_max(std::numeric_limits<float>::min()); + if (BKE_volume_min_max(volume, volume_bound_min, volume_bound_max)) { + const pxr::VtArray<pxr::GfVec3f> volume_extent = {pxr::GfVec3f(&volume_bound_min[0]), + pxr::GfVec3f(&volume_bound_max[0])}; + usd_volume.GetExtentAttr().Set(volume_extent, timecode); + } + + BKE_volume_unload(volume); +} + +std::optional<std::string> USDVolumeWriter::resolve_vdb_file(const Volume *volume) const +{ + std::optional<std::string> vdb_file_path; + if (volume->filepath[0] == '\0') { + /* Entering this section should mean that Volume object contains OpenVDB data that is not + * obtained from external .vdb file but rather generated inside of Blender (i.e. by 'Mesh to + * Volume' modifier). Try to save this data to a .vdb file. */ + + vdb_file_path = construct_vdb_file_path(volume); + if (!BKE_volume_save( + volume, usd_export_context_.bmain, NULL, vdb_file_path.value_or("").c_str())) { + return std::nullopt; + } + } + + if (!vdb_file_path.has_value()) { + vdb_file_path = BKE_volume_grids_frame_filepath(volume); + if (vdb_file_path->empty()) { + return std::nullopt; + } + } + + return vdb_file_path; +} + +std::optional<std::string> USDVolumeWriter::construct_vdb_file_path(const Volume *volume) const +{ + const std::string usd_file_path = get_export_file_path(); + if (usd_file_path.empty()) { + return std::nullopt; + } + + char usd_directory_path[FILE_MAX]; + char usd_file_name[FILE_MAXFILE]; + BLI_split_dirfile(usd_file_path.c_str(), + usd_directory_path, + usd_file_name, + sizeof(usd_directory_path), + sizeof(usd_file_name)); + + if (usd_directory_path[0] == '\0' || usd_file_name[0] == '\0') { + return std::nullopt; + } + + const char *vdb_directory_name = "volumes"; + + char vdb_directory_path[FILE_MAX]; + BLI_strncpy(vdb_directory_path, usd_directory_path, FILE_MAX); + strcat(vdb_directory_path, vdb_directory_name); + BLI_dir_create_recursive(vdb_directory_path); + + char vdb_file_name[FILE_MAXFILE]; + BLI_strncpy(vdb_file_name, volume->id.name + 2, FILE_MAXFILE); + const pxr::UsdTimeCode timecode = get_export_time_code(); + if (!timecode.IsDefault()) { + const int frame = (int)timecode.GetValue(); + const int num_frame_digits = frame == 0 ? 1 : integer_digits_i(abs(frame)); + BLI_path_frame(vdb_file_name, frame, num_frame_digits); + } + strcat(vdb_file_name, ".vdb"); + + char vdb_file_path[FILE_MAX]; + BLI_path_join(vdb_file_path, sizeof(vdb_file_path), vdb_directory_path, vdb_file_name, NULL); + + return vdb_file_path; +} + +std::optional<std::string> USDVolumeWriter::construct_vdb_relative_file_path( + const std::string &vdb_file_path) const +{ + const std::string usd_file_path = get_export_file_path(); + if (usd_file_path.empty()) { + return std::nullopt; + } + + char relative_path[FILE_MAX]; + BLI_strncpy(relative_path, vdb_file_path.c_str(), FILE_MAX); + BLI_path_rel(relative_path, usd_file_path.c_str()); + if (!BLI_path_is_rel(relative_path)) { + return std::nullopt; + } + + /* Following code was written with an assumption that Blender's relative paths start with + * // characters as well as have OS dependent slashes. Inside of USD files those relative + * paths should start with either ./ or ../ characters and have always forward slashes (/) + * separating directories. This is the convention used in USD documentation (and it seems + * to be used in other DCC packages as well). */ + std::string relative_path_processed = pxr::TfNormPath(relative_path + 2); + if (relative_path_processed[0] != '.') { + relative_path_processed.insert(0, "./"); + } + + return relative_path_processed; +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_writer_volume.h b/source/blender/io/usd/intern/usd_writer_volume.h new file mode 100644 index 00000000000..8c1e36b7e53 --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_volume.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#pragma once + +#include <optional> + +#include "BLI_math_vec_types.hh" +#include "usd_writer_abstract.h" + +struct Volume; + +namespace blender::io::usd { + +/* Writer for writing OpenVDB assets to UsdVolVolume. Volume data is stored in separate .vdb files + * which are referenced in USD file. */ +class USDVolumeWriter : public USDAbstractWriter { + public: + USDVolumeWriter(const USDExporterContext &ctx); + + protected: + virtual bool check_is_animated(const HierarchyContext &context) const override; + virtual void do_write(HierarchyContext &context) override; + + private: + /* Try to ensure that external .vdb file is available for USD to be referenced. Blender can + * either reference external OpenVDB data or generate such data internally. Latter option will + * mean that `resolve_vdb_file` method will try to export volume data to a new .vdb file. If + * successful, this method returns absolute file path to the resolved .vdb file, if not, returns + * `std::nullopt`. */ + std::optional<std::string> resolve_vdb_file(const Volume *volume) const; + + std::optional<std::string> construct_vdb_file_path(const Volume *volume) const; + std::optional<std::string> construct_vdb_relative_file_path( + const std::string &vdb_file_path) const; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index e63cd0a4e04..2e4dcb0da94 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -28,7 +28,7 @@ struct USDExportParams { bool generate_preview_surface; bool export_textures; bool overwrite_textures; - bool relative_texture_paths; + bool relative_paths; }; struct USDImportParams { @@ -83,7 +83,7 @@ int USD_get_version(void); /* USD Import and Mesh Cache interface. */ struct CacheArchiveHandle *USD_create_handle(struct Main *bmain, - const char *filename, + const char *filepath, struct ListBase *object_paths); void USD_free_handle(struct CacheArchiveHandle *handle); diff --git a/source/blender/io/wavefront_obj/CMakeLists.txt b/source/blender/io/wavefront_obj/CMakeLists.txt index 67cec000778..f7958ef4ec6 100644 --- a/source/blender/io/wavefront_obj/CMakeLists.txt +++ b/source/blender/io/wavefront_obj/CMakeLists.txt @@ -15,6 +15,7 @@ set(INC ../../makesrna ../../nodes ../../windowmanager + ../../../../extern/fast_float ../../../../extern/fmtlib/include ../../../../intern/guardedalloc ) @@ -35,6 +36,7 @@ set(SRC importer/obj_import_mesh.cc importer/obj_import_mtl.cc importer/obj_import_nurbs.cc + importer/obj_import_string_utils.cc importer/obj_importer.cc IO_wavefront_obj.h @@ -50,6 +52,7 @@ set(SRC importer/obj_import_mtl.hh importer/obj_import_nurbs.hh importer/obj_import_objects.hh + importer/obj_import_string_utils.hh importer/obj_importer.hh ) @@ -69,8 +72,10 @@ blender_add_lib(bf_wavefront_obj "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) set(TEST_SRC tests/obj_exporter_tests.cc + tests/obj_import_string_utils_tests.cc tests/obj_importer_tests.cc tests/obj_mtl_parser_tests.cc + tests/obj_exporter_tests.hh ) diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc index b027df73b1e..11d1bafdafe 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc @@ -385,7 +385,7 @@ void OBJWriter::write_edges_indices(FormatHandler<eFileType::OBJ> &fh, const IndexOffsets &offsets, const OBJMesh &obj_mesh_data) const { - /* Note: ensure_mesh_edges should be called before. */ + /* NOTE: ensure_mesh_edges should be called before. */ const int tot_edges = obj_mesh_data.tot_edges(); for (int edge_index = 0; edge_index < tot_edges; edge_index++) { const std::optional<std::array<int, 2>> vertex_indices = diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh index 0d7c089ec14..f0263989bfc 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh @@ -236,7 +236,7 @@ constexpr FormattingSyntax syntax_elem_to_formatting(const eMTLSyntaxElement key case eMTLSyntaxElement::Ke: { return {"Ke {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>}; } - /* Note: first texture map related argument, if present, will have its own leading space. */ + /* NOTE: first texture map related argument, if present, will have its own leading space. */ case eMTLSyntaxElement::map_Kd: { return {"map_Kd{} {}\n", 2, is_type_string_related<T...>}; } diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc index d14401224ed..c7990028312 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc @@ -8,9 +8,8 @@ #include "BLI_string_ref.hh" #include "BLI_vector.hh" -#include "IO_string_utils.hh" - #include "obj_import_file_reader.hh" +#include "obj_import_string_utils.hh" namespace blender::io::obj { @@ -67,40 +66,43 @@ static Geometry *create_geometry(Geometry *const prev_geometry, } static void geom_add_vertex(Geometry *geom, - const StringRef line, + const char *p, + const char *end, GlobalVertices &r_global_vertices) { float3 vert; - parse_floats(line, 0.0f, vert, 3); + parse_floats(p, end, 0.0f, vert, 3); r_global_vertices.vertices.append(vert); geom->vertex_count_++; } static void geom_add_vertex_normal(Geometry *geom, - const StringRef line, + const char *p, + const char *end, GlobalVertices &r_global_vertices) { float3 normal; - parse_floats(line, 0.0f, normal, 3); + parse_floats(p, end, 0.0f, normal, 3); r_global_vertices.vertex_normals.append(normal); geom->has_vertex_normals_ = true; } -static void geom_add_uv_vertex(const StringRef line, GlobalVertices &r_global_vertices) +static void geom_add_uv_vertex(const char *p, const char *end, GlobalVertices &r_global_vertices) { float2 uv; - parse_floats(line, 0.0f, uv, 2); + parse_floats(p, end, 0.0f, uv, 2); r_global_vertices.uv_vertices.append(uv); } static void geom_add_edge(Geometry *geom, - StringRef line, + const char *p, + const char *end, const VertexIndexOffset &offsets, GlobalVertices &r_global_vertices) { int edge_v1, edge_v2; - line = parse_int(line, -1, edge_v1); - line = parse_int(line, -1, edge_v2); + p = parse_int(p, end, -1, edge_v1); + p = parse_int(p, end, -1, edge_v2); /* Always keep stored indices non-negative and zero-based. */ edge_v1 += edge_v1 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1; edge_v2 += edge_v2 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1; @@ -109,7 +111,8 @@ static void geom_add_edge(Geometry *geom, } static void geom_add_polygon(Geometry *geom, - StringRef line, + const char *p, + const char *end, const GlobalVertices &global_vertices, const VertexIndexOffset &offsets, const int material_index, @@ -128,24 +131,24 @@ static void geom_add_polygon(Geometry *geom, curr_face.start_index_ = orig_corners_size; bool face_valid = true; - line = drop_whitespace(line); - while (!line.is_empty() && face_valid) { + p = drop_whitespace(p, end); + while (p < end && face_valid) { PolyCorner corner; bool got_uv = false, got_normal = false; /* Parse vertex index. */ - line = parse_int(line, INT32_MAX, corner.vert_index, false); + p = parse_int(p, end, INT32_MAX, corner.vert_index, false); face_valid &= corner.vert_index != INT32_MAX; - if (!line.is_empty() && line[0] == '/') { + if (p < end && *p == '/') { /* Parse UV index. */ - line = line.drop_prefix(1); - if (!line.is_empty() && line[0] != '/') { - line = parse_int(line, INT32_MAX, corner.uv_vert_index, false); + ++p; + if (p < end && *p != '/') { + p = parse_int(p, end, INT32_MAX, corner.uv_vert_index, false); got_uv = corner.uv_vert_index != INT32_MAX; } /* Parse normal index. */ - if (!line.is_empty() && line[0] == '/') { - line = line.drop_prefix(1); - line = parse_int(line, INT32_MAX, corner.vertex_normal_index, false); + if (p < end && *p == '/') { + ++p; + p = parse_int(p, end, INT32_MAX, corner.vertex_normal_index, false); got_normal = corner.uv_vert_index != INT32_MAX; } } @@ -186,7 +189,7 @@ static void geom_add_polygon(Geometry *geom, curr_face.corner_count_++; /* Skip whitespace to get to the next face corner. */ - line = drop_whitespace(line); + p = drop_whitespace(p, end); } if (face_valid) { @@ -201,14 +204,16 @@ static void geom_add_polygon(Geometry *geom, } static Geometry *geom_set_curve_type(Geometry *geom, - const StringRef rest_line, + const char *p, + const char *end, const GlobalVertices &global_vertices, const StringRef group_name, VertexIndexOffset &r_offsets, Vector<std::unique_ptr<Geometry>> &r_all_geometries) { - if (rest_line.find("bspline") == StringRef::not_found) { - std::cerr << "Curve type not supported:'" << rest_line << "'" << std::endl; + p = drop_whitespace(p, end); + if (!StringRef(p, end).startswith("bspline")) { + std::cerr << "Curve type not supported: '" << std::string(p, end) << "'" << std::endl; return geom; } geom = create_geometry( @@ -217,22 +222,23 @@ static Geometry *geom_set_curve_type(Geometry *geom, return geom; } -static void geom_set_curve_degree(Geometry *geom, const StringRef line) +static void geom_set_curve_degree(Geometry *geom, const char *p, const char *end) { - parse_int(line, 3, geom->nurbs_element_.degree); + parse_int(p, end, 3, geom->nurbs_element_.degree); } static void geom_add_curve_vertex_indices(Geometry *geom, - StringRef line, + const char *p, + const char *end, const GlobalVertices &global_vertices) { /* Curve lines always have "0.0" and "1.0", skip over them. */ float dummy[2]; - line = parse_floats(line, 0, dummy, 2); + p = parse_floats(p, end, 0, dummy, 2); /* Parse indices. */ - while (!line.is_empty()) { + while (p < end) { int index; - line = parse_int(line, INT32_MAX, index); + p = parse_int(p, end, INT32_MAX, index); if (index == INT32_MAX) { return; } @@ -242,22 +248,22 @@ static void geom_add_curve_vertex_indices(Geometry *geom, } } -static void geom_add_curve_parameters(Geometry *geom, StringRef line) +static void geom_add_curve_parameters(Geometry *geom, const char *p, const char *end) { - line = drop_whitespace(line); - if (line.is_empty()) { - std::cerr << "Invalid OBJ curve parm line: '" << line << "'" << std::endl; + p = drop_whitespace(p, end); + if (p == end) { + std::cerr << "Invalid OBJ curve parm line" << std::endl; return; } - if (line[0] != 'u') { - std::cerr << "OBJ curve surfaces are not supported: '" << line[0] << "'" << std::endl; + if (*p != 'u') { + std::cerr << "OBJ curve surfaces are not supported: '" << *p << "'" << std::endl; return; } - line = line.drop_prefix(1); + ++p; - while (!line.is_empty()) { + while (p < end) { float val; - line = parse_float(line, FLT_MAX, val); + p = parse_float(p, end, FLT_MAX, val); if (val != FLT_MAX) { geom->nurbs_element_.parm.append(val); } @@ -270,7 +276,6 @@ static void geom_add_curve_parameters(Geometry *geom, StringRef line) static void geom_update_group(const StringRef rest_line, std::string &r_group_name) { - if (rest_line.find("off") != string::npos || rest_line.find("null") != string::npos || rest_line.find("default") != string::npos) { /* Set group for future elements like faces or curves to empty. */ @@ -280,17 +285,18 @@ static void geom_update_group(const StringRef rest_line, std::string &r_group_na r_group_name = rest_line; } -static void geom_update_smooth_group(StringRef line, bool &r_state_shaded_smooth) +static void geom_update_smooth_group(const char *p, const char *end, bool &r_state_shaded_smooth) { - line = drop_whitespace(line); + p = drop_whitespace(p, end); /* Some implementations use "0" and "null" too, in addition to "off". */ + const StringRef line = StringRef(p, end); if (line == "0" || line.startswith("off") || line.startswith("null")) { r_state_shaded_smooth = false; return; } int smooth = 0; - parse_int(line, 0, smooth); + parse_int(p, end, 0, smooth); r_state_shaded_smooth = smooth != 0; } @@ -312,21 +318,21 @@ OBJParser::~OBJParser() } /* If line starts with keyword followed by whitespace, returns true and drops it from the line. */ -static bool parse_keyword(StringRef &line, StringRef keyword) +static bool parse_keyword(const char *&p, const char *end, StringRef keyword) { const size_t keyword_len = keyword.size(); - if (line.size() < keyword_len + 1) { + if (end - p < keyword_len + 1) { return false; } - if (!line.startswith(keyword)) { + if (memcmp(p, keyword.data(), keyword_len) != 0) { return false; } - /* Treat any ASCII control character as whitespace; don't use isspace() for performance reasons. - */ - if (line[keyword_len] > ' ') { + /* Treat any ASCII control character as white-space; + * don't use `isspace()` for performance reasons. */ + if (p[keyword_len] > ' ') { return false; } - line = line.drop_prefix(keyword_len + 1); + p += keyword_len + 1; return true; } @@ -400,27 +406,29 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, StringRef buffer_str{buffer.data(), (int64_t)last_nl}; while (!buffer_str.is_empty()) { StringRef line = read_next_line(buffer_str); - line = drop_whitespace(line); + const char *p = line.begin(), *end = line.end(); + p = drop_whitespace(p, end); ++line_number; - if (line.is_empty()) { + if (p == end) { continue; } /* Most common things that start with 'v': vertices, normals, UVs. */ - if (line[0] == 'v') { - if (parse_keyword(line, "v")) { - geom_add_vertex(curr_geom, line, r_global_vertices); + if (*p == 'v') { + if (parse_keyword(p, end, "v")) { + geom_add_vertex(curr_geom, p, end, r_global_vertices); } - else if (parse_keyword(line, "vn")) { - geom_add_vertex_normal(curr_geom, line, r_global_vertices); + else if (parse_keyword(p, end, "vn")) { + geom_add_vertex_normal(curr_geom, p, end, r_global_vertices); } - else if (parse_keyword(line, "vt")) { - geom_add_uv_vertex(line, r_global_vertices); + else if (parse_keyword(p, end, "vt")) { + geom_add_uv_vertex(p, end, r_global_vertices); } } /* Faces. */ - else if (parse_keyword(line, "f")) { + else if (parse_keyword(p, end, "f")) { geom_add_polygon(curr_geom, - line, + p, + end, r_global_vertices, offsets, state_material_index, @@ -428,20 +436,24 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, state_shaded_smooth); } /* Faces. */ - else if (parse_keyword(line, "l")) { - geom_add_edge(curr_geom, line, offsets, r_global_vertices); + else if (parse_keyword(p, end, "l")) { + geom_add_edge(curr_geom, p, end, offsets, r_global_vertices); } /* Objects. */ - else if (parse_keyword(line, "o")) { + else if (parse_keyword(p, end, "o")) { state_shaded_smooth = false; state_group_name = ""; state_material_name = ""; - curr_geom = create_geometry( - curr_geom, GEOM_MESH, line.trim(), r_global_vertices, r_all_geometries, offsets); + curr_geom = create_geometry(curr_geom, + GEOM_MESH, + StringRef(p, end).trim(), + r_global_vertices, + r_all_geometries, + offsets); } /* Groups. */ - else if (parse_keyword(line, "g")) { - geom_update_group(line.trim(), state_group_name); + else if (parse_keyword(p, end, "g")) { + geom_update_group(StringRef(p, end).trim(), state_group_name); int new_index = curr_geom->group_indices_.size(); state_group_index = curr_geom->group_indices_.lookup_or_add(state_group_name, new_index); if (new_index == state_group_index) { @@ -449,12 +461,12 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, } } /* Smoothing groups. */ - else if (parse_keyword(line, "s")) { - geom_update_smooth_group(line, state_shaded_smooth); + else if (parse_keyword(p, end, "s")) { + geom_update_smooth_group(p, end, state_shaded_smooth); } /* Materials and their libraries. */ - else if (parse_keyword(line, "usemtl")) { - state_material_name = line.trim(); + else if (parse_keyword(p, end, "usemtl")) { + state_material_name = StringRef(p, end).trim(); int new_mat_index = curr_geom->material_indices_.size(); state_material_index = curr_geom->material_indices_.lookup_or_add(state_material_name, new_mat_index); @@ -462,32 +474,32 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, curr_geom->material_order_.append(state_material_name); } } - else if (parse_keyword(line, "mtllib")) { - add_mtl_library(line.trim()); + else if (parse_keyword(p, end, "mtllib")) { + add_mtl_library(StringRef(p, end).trim()); } /* Comments. */ - else if (line.startswith("#")) { + else if (*p == '#') { /* Nothing to do. */ } /* Curve related things. */ - else if (parse_keyword(line, "cstype")) { + else if (parse_keyword(p, end, "cstype")) { curr_geom = geom_set_curve_type( - curr_geom, line, r_global_vertices, state_group_name, offsets, r_all_geometries); + curr_geom, p, end, r_global_vertices, state_group_name, offsets, r_all_geometries); } - else if (parse_keyword(line, "deg")) { - geom_set_curve_degree(curr_geom, line); + else if (parse_keyword(p, end, "deg")) { + geom_set_curve_degree(curr_geom, p, end); } - else if (parse_keyword(line, "curv")) { - geom_add_curve_vertex_indices(curr_geom, line, r_global_vertices); + else if (parse_keyword(p, end, "curv")) { + geom_add_curve_vertex_indices(curr_geom, p, end, r_global_vertices); } - else if (parse_keyword(line, "parm")) { - geom_add_curve_parameters(curr_geom, line); + else if (parse_keyword(p, end, "parm")) { + geom_add_curve_parameters(curr_geom, p, end); } - else if (line.startswith("end")) { + else if (StringRef(p, end).startswith("end")) { /* End of curve definition, nothing else to do. */ } else { - std::cout << "OBJ element not recognized: '" << line << "'" << std::endl; + std::cout << "OBJ element not recognized: '" << std::string(p, end) << "'" << std::endl; } } @@ -501,33 +513,33 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, add_default_mtl_library(); } -static eMTLSyntaxElement mtl_line_start_to_enum(StringRef &line) +static eMTLSyntaxElement mtl_line_start_to_enum(const char *&p, const char *end) { - if (parse_keyword(line, "map_Kd")) { + if (parse_keyword(p, end, "map_Kd")) { return eMTLSyntaxElement::map_Kd; } - if (parse_keyword(line, "map_Ks")) { + if (parse_keyword(p, end, "map_Ks")) { return eMTLSyntaxElement::map_Ks; } - if (parse_keyword(line, "map_Ns")) { + if (parse_keyword(p, end, "map_Ns")) { return eMTLSyntaxElement::map_Ns; } - if (parse_keyword(line, "map_d")) { + if (parse_keyword(p, end, "map_d")) { return eMTLSyntaxElement::map_d; } - if (parse_keyword(line, "refl")) { + if (parse_keyword(p, end, "refl")) { return eMTLSyntaxElement::map_refl; } - if (parse_keyword(line, "map_refl")) { + if (parse_keyword(p, end, "map_refl")) { return eMTLSyntaxElement::map_refl; } - if (parse_keyword(line, "map_Ke")) { + if (parse_keyword(p, end, "map_Ke")) { return eMTLSyntaxElement::map_Ke; } - if (parse_keyword(line, "bump")) { + if (parse_keyword(p, end, "bump")) { return eMTLSyntaxElement::map_Bump; } - if (parse_keyword(line, "map_Bump") || parse_keyword(line, "map_bump")) { + if (parse_keyword(p, end, "map_Bump") || parse_keyword(p, end, "map_bump")) { return eMTLSyntaxElement::map_Bump; } return eMTLSyntaxElement::string; @@ -545,39 +557,43 @@ static const std::pair<StringRef, int> unsupported_texture_options[] = { {"-texres", 1}, }; -static bool parse_texture_option(StringRef &line, MTLMaterial *material, tex_map_XX &tex_map) +static bool parse_texture_option(const char *&p, + const char *end, + MTLMaterial *material, + tex_map_XX &tex_map) { - line = drop_whitespace(line); - if (parse_keyword(line, "-o")) { - line = parse_floats(line, 0.0f, tex_map.translation, 3); + p = drop_whitespace(p, end); + if (parse_keyword(p, end, "-o")) { + p = parse_floats(p, end, 0.0f, tex_map.translation, 3); return true; } - if (parse_keyword(line, "-s")) { - line = parse_floats(line, 1.0f, tex_map.scale, 3); + if (parse_keyword(p, end, "-s")) { + p = parse_floats(p, end, 1.0f, tex_map.scale, 3); return true; } - if (parse_keyword(line, "-bm")) { - line = parse_float(line, 1.0f, material->map_Bump_strength); + if (parse_keyword(p, end, "-bm")) { + p = parse_float(p, end, 1.0f, material->map_Bump_strength); return true; } - if (parse_keyword(line, "-type")) { - line = drop_whitespace(line); + if (parse_keyword(p, end, "-type")) { + p = drop_whitespace(p, end); /* Only sphere is supported. */ tex_map.projection_type = SHD_PROJ_SPHERE; + const StringRef line = StringRef(p, end); if (!line.startswith("sphere")) { std::cerr << "OBJ import: only sphere MTL projection type is supported: '" << line << "'" << std::endl; } - line = drop_non_whitespace(line); + p = drop_non_whitespace(p, end); return true; } /* Check for unsupported options and skip them. */ for (const auto &opt : unsupported_texture_options) { - if (parse_keyword(line, opt.first)) { + if (parse_keyword(p, end, opt.first)) { /* Drop the arguments. */ for (int i = 0; i < opt.second; ++i) { - line = drop_whitespace(line); - line = drop_non_whitespace(line); + p = drop_whitespace(p, end); + p = drop_non_whitespace(p, end); } return true; } @@ -586,15 +602,19 @@ static bool parse_texture_option(StringRef &line, MTLMaterial *material, tex_map return false; } -static void parse_texture_map(StringRef line, MTLMaterial *material, const char *mtl_dir_path) +static void parse_texture_map(const char *p, + const char *end, + MTLMaterial *material, + const char *mtl_dir_path) { + const StringRef line = StringRef(p, end); bool is_map = line.startswith("map_"); bool is_refl = line.startswith("refl"); bool is_bump = line.startswith("bump"); if (!is_map && !is_refl && !is_bump) { return; } - eMTLSyntaxElement key = mtl_line_start_to_enum(line); + eMTLSyntaxElement key = mtl_line_start_to_enum(p, end); if (key == eMTLSyntaxElement::string || !material->texture_maps.contains(key)) { /* No supported texture map found. */ std::cerr << "OBJ import: MTL texture map type not supported: '" << line << "'" << std::endl; @@ -604,12 +624,11 @@ static void parse_texture_map(StringRef line, MTLMaterial *material, const char tex_map.mtl_dir_path = mtl_dir_path; /* Parse texture map options. */ - while (parse_texture_option(line, material, tex_map)) { + while (parse_texture_option(p, end, material, tex_map)) { } /* What remains is the image path. */ - line = line.trim(); - tex_map.image_path = line; + tex_map.image_path = StringRef(p, end).trim(); } Span<std::string> OBJParser::mtl_libraries() const @@ -667,51 +686,53 @@ void MTLParser::parse_and_store(Map<string, std::unique_ptr<MTLMaterial>> &r_mat StringRef buffer_str{(const char *)buffer, (int64_t)buffer_len}; while (!buffer_str.is_empty()) { - StringRef line = read_next_line(buffer_str); - line = drop_whitespace(line); - if (line.is_empty()) { + const StringRef line = read_next_line(buffer_str); + const char *p = line.begin(), *end = line.end(); + p = drop_whitespace(p, end); + if (p == end) { continue; } - if (parse_keyword(line, "newmtl")) { - line = line.trim(); - if (r_materials.contains(line)) { + if (parse_keyword(p, end, "newmtl")) { + StringRef mat_name = StringRef(p, end).trim(); + if (r_materials.contains(mat_name)) { material = nullptr; } else { - material = r_materials.lookup_or_add(string(line), std::make_unique<MTLMaterial>()).get(); + material = + r_materials.lookup_or_add(string(mat_name), std::make_unique<MTLMaterial>()).get(); } } else if (material != nullptr) { - if (parse_keyword(line, "Ns")) { - parse_float(line, 324.0f, material->Ns); + if (parse_keyword(p, end, "Ns")) { + parse_float(p, end, 324.0f, material->Ns); } - else if (parse_keyword(line, "Ka")) { - parse_floats(line, 0.0f, material->Ka, 3); + else if (parse_keyword(p, end, "Ka")) { + parse_floats(p, end, 0.0f, material->Ka, 3); } - else if (parse_keyword(line, "Kd")) { - parse_floats(line, 0.8f, material->Kd, 3); + else if (parse_keyword(p, end, "Kd")) { + parse_floats(p, end, 0.8f, material->Kd, 3); } - else if (parse_keyword(line, "Ks")) { - parse_floats(line, 0.5f, material->Ks, 3); + else if (parse_keyword(p, end, "Ks")) { + parse_floats(p, end, 0.5f, material->Ks, 3); } - else if (parse_keyword(line, "Ke")) { - parse_floats(line, 0.0f, material->Ke, 3); + else if (parse_keyword(p, end, "Ke")) { + parse_floats(p, end, 0.0f, material->Ke, 3); } - else if (parse_keyword(line, "Ni")) { - parse_float(line, 1.45f, material->Ni); + else if (parse_keyword(p, end, "Ni")) { + parse_float(p, end, 1.45f, material->Ni); } - else if (parse_keyword(line, "d")) { - parse_float(line, 1.0f, material->d); + else if (parse_keyword(p, end, "d")) { + parse_float(p, end, 1.0f, material->d); } - else if (parse_keyword(line, "illum")) { + else if (parse_keyword(p, end, "illum")) { /* Some files incorrectly use a float (T60135). */ float val; - parse_float(line, 1.0f, val); + parse_float(p, end, 1.0f, val); material->illum = val; } else { - parse_texture_map(line, material, mtl_dir_path_); + parse_texture_map(p, end, material, mtl_dir_path_); } } } diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh index e41a7f8518e..8bfc5fe8bf0 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh @@ -13,7 +13,7 @@ namespace blender::io::obj { -/* Note: the OBJ parser implementation is planned to get fairly large changes "soon", +/* NOTE: the OBJ parser implementation is planned to get fairly large changes "soon", * so don't read too much into current implementation... */ class OBJParser { private: diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc index c2ecd8a37de..f39def0a4af 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc @@ -13,13 +13,12 @@ #include "DNA_material_types.h" #include "DNA_node_types.h" -#include "IO_string_utils.hh" - #include "NOD_shader.h" /* TODO: move eMTLSyntaxElement out of following file into a more neutral place */ #include "obj_export_io.hh" #include "obj_import_mtl.hh" +#include "obj_import_string_utils.hh" namespace blender::io::obj { diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc new file mode 100644 index 00000000000..c8eaa046e68 --- /dev/null +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "obj_import_string_utils.hh" + +/* NOTE: we could use C++17 <charconv> from_chars to parse + * floats, but even if some compilers claim full support, + * their standard libraries are not quite there yet. + * LLVM/libc++ only has a float parser since LLVM 14, + * and gcc/libstdc++ since 11.1. So until at least these are + * the minimum spec, use an external library. */ +#include "fast_float.h" +#include <charconv> + +namespace blender::io::obj { + +StringRef read_next_line(StringRef &buffer) +{ + const char *start = buffer.begin(); + const char *end = buffer.end(); + size_t len = 0; + char prev = 0; + const char *ptr = start; + while (ptr < end) { + char c = *ptr++; + if (c == '\n' && prev != '\\') { + break; + } + prev = c; + ++len; + } + + buffer = StringRef(ptr, end); + return StringRef(start, len); +} + +static bool is_whitespace(char c) +{ + return c <= ' ' || c == '\\'; +} + +const char *drop_whitespace(const char *p, const char *end) +{ + while (p < end && is_whitespace(*p)) { + ++p; + } + return p; +} + +const char *drop_non_whitespace(const char *p, const char *end) +{ + while (p < end && !is_whitespace(*p)) { + ++p; + } + return p; +} + +static const char *drop_plus(const char *p, const char *end) +{ + if (p < end && *p == '+') { + ++p; + } + return p; +} + +const char *parse_float( + const char *p, const char *end, float fallback, float &dst, bool skip_space) +{ + if (skip_space) { + p = drop_whitespace(p, end); + } + p = drop_plus(p, end); + fast_float::from_chars_result res = fast_float::from_chars(p, end, dst); + if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) { + dst = fallback; + } + return res.ptr; +} + +const char *parse_floats(const char *p, const char *end, float fallback, float *dst, int count) +{ + for (int i = 0; i < count; ++i) { + p = parse_float(p, end, fallback, dst[i]); + } + return p; +} + +const char *parse_int(const char *p, const char *end, int fallback, int &dst, bool skip_space) +{ + if (skip_space) { + p = drop_whitespace(p, end); + } + p = drop_plus(p, end); + std::from_chars_result res = std::from_chars(p, end, dst); + if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) { + dst = fallback; + } + return res.ptr; +} + +} // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh new file mode 100644 index 00000000000..3f428b1ab5c --- /dev/null +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +/* + * Various text parsing utilities used by OBJ importer. + * The utilities are not directly usable by other formats, since + * they treat backslash (\) as a whitespace character (OBJ format + * allows backslashes to function as a line-continuation character). + * + * Many of these functions take two pointers (p, end) indicating + * which part of a string to operate on, and return a possibly + * changed new start of the string. They could be taking a StringRef + * as input and returning a new StringRef, but this is a hot path + * in OBJ parsing, and the StringRef approach does lose performance + * (mostly due to return of StringRef being two register-size values + * instead of just one pointer). + */ + +namespace blender::io::obj { + +/** + * Fetches next line from an input string buffer. + * + * The returned line will not have '\n' characters at the end; + * the `buffer` is modified to contain remaining text without + * the input line. + * + * Note that backslash (\) character is treated as a line + * continuation. + */ +StringRef read_next_line(StringRef &buffer); + +/** + * Drop leading white-space from a string part. + * Note that backslash character is considered white-space. + */ +const char *drop_whitespace(const char *p, const char *end); + +/** + * Drop leading non-white-space from a string part. + * Note that backslash character is considered white-space. + */ +const char *drop_non_whitespace(const char *p, const char *end); + +/** + * Parse an integer from an input string. + * The parsed result is stored in `dst`. The function skips + * leading white-space unless `skip_space=false`. If the + * number can't be parsed (invalid syntax, out of range), + * `fallback` value is stored instead. + * + * Returns the start of remainder of the input string after parsing. + */ +const char *parse_int( + const char *p, const char *end, int fallback, int &dst, bool skip_space = true); + +/** + * Parse a float from an input string. + * The parsed result is stored in `dst`. The function skips + * leading white-space unless `skip_space=false`. If the + * number can't be parsed (invalid syntax, out of range), + * `fallback` value is stored instead. + * + * Returns the start of remainder of the input string after parsing. + */ +const char *parse_float( + const char *p, const char *end, float fallback, float &dst, bool skip_space = true); + +/** + * Parse a number of white-space separated floats from an input string. + * The parsed `count` numbers are stored in `dst`. If a + * number can't be parsed (invalid syntax, out of range), + * `fallback` value is stored instead. + * + * Returns the start of remainder of the input string after parsing. + */ +const char *parse_floats(const char *p, const char *end, float fallback, float *dst, int count); + +} // namespace blender::io::obj diff --git a/source/blender/io/common/intern/string_utils_test.cc b/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc index a78bd7ab8a3..46e093bb8a7 100644 --- a/source/blender/io/common/intern/string_utils_test.cc +++ b/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc @@ -1,14 +1,14 @@ /* SPDX-License-Identifier: Apache-2.0 */ -#include "IO_string_utils.hh" +#include "obj_import_string_utils.hh" #include "testing/testing.h" -namespace blender::io { +namespace blender::io::obj { #define EXPECT_STRREF_EQ(str1, str2) EXPECT_STREQ(str1, std::string(str2).c_str()) -TEST(string_utils, read_next_line) +TEST(obj_import_string_utils, read_next_line) { std::string str = "abc\n \n\nline with \\\ncontinuation\nCRLF ending:\r\na"; StringRef s = str; @@ -21,7 +21,20 @@ TEST(string_utils, read_next_line) EXPECT_TRUE(s.is_empty()); } -TEST(string_utils, drop_whitespace) +static StringRef drop_whitespace(StringRef s) +{ + return StringRef(drop_whitespace(s.begin(), s.end()), s.end()); +} +static StringRef parse_int(StringRef s, int fallback, int &dst, bool skip_space = true) +{ + return StringRef(parse_int(s.begin(), s.end(), fallback, dst, skip_space), s.end()); +} +static StringRef parse_float(StringRef s, float fallback, float &dst, bool skip_space = true) +{ + return StringRef(parse_float(s.begin(), s.end(), fallback, dst, skip_space), s.end()); +} + +TEST(obj_import_string_utils, drop_whitespace) { /* Empty */ EXPECT_STRREF_EQ("", drop_whitespace("")); @@ -39,7 +52,7 @@ TEST(string_utils, drop_whitespace) EXPECT_STRREF_EQ("d", drop_whitespace(" \\ d")); } -TEST(string_utils, parse_int_valid) +TEST(obj_import_string_utils, parse_int_valid) { std::string str = "1 -10 \t 1234 1234567890 +7 123a"; StringRef s = str; @@ -59,7 +72,7 @@ TEST(string_utils, parse_int_valid) EXPECT_STRREF_EQ("a", s); } -TEST(string_utils, parse_int_invalid) +TEST(obj_import_string_utils, parse_int_invalid) { int val; /* Invalid syntax */ @@ -75,7 +88,7 @@ TEST(string_utils, parse_int_invalid) EXPECT_EQ(val, -4); } -TEST(string_utils, parse_float_valid) +TEST(obj_import_string_utils, parse_float_valid) { std::string str = "1 -10 123.5 -17.125 0.1 1e6 50.0e-1"; StringRef s = str; @@ -97,7 +110,7 @@ TEST(string_utils, parse_float_valid) EXPECT_TRUE(s.is_empty()); } -TEST(string_utils, parse_float_invalid) +TEST(obj_import_string_utils, parse_float_invalid) { float val; /* Invalid syntax */ @@ -115,4 +128,4 @@ TEST(string_utils, parse_float_invalid) EXPECT_EQ(val, -4.0f); } -} // namespace blender::io +} // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc index 3d34fb6f9c6..d7f4ce3d773 100644 --- a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc @@ -333,7 +333,7 @@ TEST_F(obj_importer_test, import_invalid_syntax) {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, {"OBObjectWithAReallyLongNameToCheckHowImportHandlesNamesThatAreLon", OB_MESH, - 10, /* Note: right now parses some invalid obj syntax as valid vertices. */ + 10, /* NOTE: right now parses some invalid obj syntax as valid vertices. */ 3, 1, 3, diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index 99737aa3b67..c1dfab8a041 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -304,7 +304,7 @@ typedef struct DriverTarget { char *rna_path; /** - * Name of the posebone to use + * Name of the pose-bone to use * (for vars where DTAR_FLAG_STRUCT_REF is used) - `MAX_ID_NAME - 2`. */ char pchan_name[64]; @@ -918,9 +918,9 @@ typedef struct KS_Path { /** Index that path affects. */ int array_index; - /** (eInsertKeyFlags) settings to supply insertkey() with. */ + /** (#eInsertKeyFlags) settings to supply insert-key() with. */ short keyingflag; - /** (eInsertKeyFlags) for each flag set, the relevant keyingflag bit overrides the default. */ + /** (#eInsertKeyFlags) for each flag set, the relevant keying-flag bit overrides the default. */ short keyingoverride; } KS_Path; diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 3e7a4431bf5..f409d1c0442 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -305,6 +305,12 @@ typedef enum eGP_Sculpt_Mode_Flag { GP_SCULPT_FLAGMODE_APPLY_THICKNESS = (1 << 2), /* apply brush to uv data */ GP_SCULPT_FLAGMODE_APPLY_UV = (1 << 3), + /* Stroke Auto-Masking for sculpt. */ + GP_SCULPT_FLAGMODE_AUTOMASK_STROKE = (1 << 4), + /* Layer Auto-Masking for sculpt. */ + GP_SCULPT_FLAGMODE_AUTOMASK_LAYER = (1 << 5), + /* Material Auto-Masking for sculpt. */ + GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL = (1 << 6), } eGP_Sculpt_Mode_Flag; typedef enum eAutomasking_flag { @@ -612,6 +618,7 @@ typedef enum eBrushCurvesSculptFlag { BRUSH_CURVES_SCULPT_FLAG_GROW_SHRINK_INVERT = (1 << 1), BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH = (1 << 2), BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE = (1 << 3), + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT = (1 << 4), } eBrushCurvesSculptFlag; #define MAX_BRUSH_PIXEL_RADIUS 500 diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 7d230b7d7a3..24e77ecf87f 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -140,6 +140,8 @@ typedef struct BrushGpencilSettings { typedef struct BrushCurvesSculptSettings { /** Number of curves added by the add brush. */ int add_amount; + /** Number of control points in new curves added by the add brush. */ + int points_per_curve; /* eBrushCurvesSculptFlag. */ uint32_t flag; /** When shrinking curves, they shouldn't become shorter than this length. */ diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h index bb53dbafdc8..2388f04cc39 100644 --- a/source/blender/makesdna/DNA_curves_types.h +++ b/source/blender/makesdna/DNA_curves_types.h @@ -9,6 +9,8 @@ #include "DNA_ID.h" #include "DNA_customdata_types.h" +#include "BLI_utildefines.h" + #ifdef __cplusplus extern "C" { #endif @@ -87,6 +89,9 @@ typedef struct CurvesGeometry { * this array is allocated with a length one larger than the number of curves. This is allowed * to be null when there are no curves. * + * Every curve offset must be at least one larger than the previous. + * In other words, every curve must have at least one point. + * * \note This is *not* stored in #CustomData because its size is one larger than #curve_data. */ int *curve_offsets; @@ -105,11 +110,11 @@ typedef struct CurvesGeometry { /** * The total number of control points in all curves. */ - int point_size; + int point_num; /** * The number of curves in the data-block. */ - int curve_size; + int curve_num; /** * Runtime data for curves, stored as a pointer to allow defining this as a C++ class. @@ -130,7 +135,13 @@ typedef struct Curves { /* Materials. */ struct Material **mat; short totcol; - short _pad2[3]; + + /** + * User-defined symmetry flag (#eCurvesSymmetryType) that causes editing operations to maintain + * symmetrical geometry. + */ + char symmetry; + char _pad2[5]; /** * Used as base mesh when curves represent e.g. hair or fur. This surface is used in edit modes. @@ -150,6 +161,14 @@ enum { HA_DS_EXPAND = (1 << 0), }; +/** #Curves.symmetry */ +typedef enum eCurvesSymmetryType { + CURVES_SYMMETRY_X = 1 << 0, + CURVES_SYMMETRY_Y = 1 << 1, + CURVES_SYMMETRY_Z = 1 << 2, +} eCurvesSymmetryType; +ENUM_OPERATORS(eCurvesSymmetryType, CURVES_SYMMETRY_Z) + /* Only one material supported currently. */ #define CURVES_MATERIAL_NR 1 diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 750f97bb3c6..70bbf5b4a49 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -303,7 +303,7 @@ .crease_threshold = DEG2RAD(140.0f), \ .calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES | \ LRT_USE_CREASE_ON_SHARP_EDGES | LRT_FILTER_FACE_MARK_KEEP_CONTOUR, \ - .angle_splitting_threshold = DEG2RAD(60.0f), \ + .angle_splitting_threshold = 0.0f, /* Do not split by default, this is for better chaining quality. */ \ .chaining_image_threshold = 0.001f, \ .chain_smooth_tolerance = 0.2f,\ .stroke_depth_offset = 0.05,\ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index b0e7342c9cb..535533565dd 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -102,7 +102,8 @@ typedef struct NoiseGpencilModifierData { /** Noise Frequency scaling */ float noise_scale; float noise_offset; - char _pad[4]; + short noise_mode; + char _pad[2]; /** How many frames before recalculate randoms. */ int step; /** Custom index for passes. */ @@ -127,6 +128,11 @@ typedef enum eNoiseGpencil_Flag { GP_NOISE_INVERT_MATERIAL = (1 << 11), } eNoiseGpencil_Flag; +typedef enum eNoiseRandomGpencil_Mode { + GP_NOISE_RANDOM_STEP = 0, + GP_NOISE_RANDOM_KEYFRAME = 1, +} eNoiseRandomGpencil_Mode; + typedef struct SubdivGpencilModifierData { GpencilModifierData modifier; /** Material for filtering. */ diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 4a1b639122a..a83262d7639 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -245,7 +245,7 @@ typedef struct bGPDstroke_Runtime { /** Vertex offset in the VBO where this stroke starts. */ int stroke_start; - /** Triangle offset in the ibo where this fill starts. */ + /** Triangle offset in the IBO where this fill starts. */ int fill_start; /** Curve Handles offset in the IBO where this handle starts. */ int curve_start; @@ -814,10 +814,10 @@ typedef enum eGPdata_Flag { /* Vertex Paint Mode - Toggle paint mode */ GP_DATA_STROKE_VERTEXMODE = (1 << 18), - /* Autolock not active layers */ + /* Auto-lock not active layers. */ GP_DATA_AUTOLOCK_LAYERS = (1 << 20), - /* Enable Bezier Editing Curve (a submode of Edit mode). */ + /* Enable Bezier Editing Curve (a sub-mode of Edit mode). */ GP_DATA_CURVE_EDIT_MODE = (1 << 21), /* Use adaptive curve resolution */ GP_DATA_CURVE_ADAPTIVE_RESOLUTION = (1 << 22), diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 4e66e2446f0..80f592bb66d 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -64,6 +64,11 @@ typedef struct ImageView { typedef struct ImagePackedFile { struct ImagePackedFile *next, *prev; struct PackedFile *packedfile; + + /* Which view and tile this ImagePackedFile represents. Normal images will use 0 and 1001 + * respectively when creating their ImagePackedFile. Must be provided for each packed image. */ + int view; + int tile_number; /** 1024 = FILE_MAX. */ char filepath[1024]; } ImagePackedFile; diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h index d5f7e25bb80..4ee5f34fcde 100644 --- a/source/blender/makesdna/DNA_layer_types.h +++ b/source/blender/makesdna/DNA_layer_types.h @@ -125,7 +125,7 @@ typedef struct ViewLayerAOV { int type; } ViewLayerAOV; -/* Lightgroup Renderpass definition. */ +/** Light-group Render-pass definition. */ typedef struct ViewLayerLightgroup { struct ViewLayerLightgroup *next, *prev; diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h index 2e446427cc3..5b3a23000d7 100644 --- a/source/blender/makesdna/DNA_lineart_types.h +++ b/source/blender/makesdna/DNA_lineart_types.h @@ -21,7 +21,7 @@ typedef enum eLineartMainFlags { LRT_ALLOW_DUPLI_OBJECTS = (1 << 2), LRT_ALLOW_OVERLAPPING_EDGES = (1 << 3), LRT_ALLOW_CLIPPING_BOUNDARIES = (1 << 4), - LRT_REMOVE_DOUBLES = (1 << 5), + /* LRT_REMOVE_DOUBLES = (1 << 5), Deprecated */ LRT_LOOSE_AS_CONTOUR = (1 << 6), LRT_GPENCIL_INVERT_SOURCE_VGROUP = (1 << 7), LRT_GPENCIL_MATCH_OUTPUT_VGROUP = (1 << 8), @@ -47,9 +47,18 @@ typedef enum eLineartEdgeFlag { LRT_EDGE_FLAG_MATERIAL = (1 << 3), LRT_EDGE_FLAG_INTERSECTION = (1 << 4), LRT_EDGE_FLAG_LOOSE = (1 << 5), - LRT_EDGE_FLAG_CHAIN_PICKED = (1 << 6), - LRT_EDGE_FLAG_CLIPPED = (1 << 7), - /** Limited to 8 bits, DON'T ADD ANYMORE until improvements on the data structure. */ + /* LRT_EDGE_FLAG_FOR_FUTURE = (1 << 7), */ + /* Limited to 8 bits for edge type flag, don't add anymore because BMEdge->head.eflag only has 8 + bits. So unless we changed this into a non-single-bit flag thing, we keep it this way. */ + /** Also used as discarded line mark. */ + LRT_EDGE_FLAG_CHAIN_PICKED = (1 << 8), + LRT_EDGE_FLAG_CLIPPED = (1 << 9), + /** Limited to 16 bits for the entire thing. */ + + /** For object loading code to use only. */ + LRT_EDGE_FLAG_INHIBIT = (1 << 14), + /** For discarding duplicated edge types in culling stage. */ + LRT_EDGE_FLAG_NEXT_IS_DUPLICATION = (1 << 15), } eLineartEdgeFlag; #define LRT_EDGE_FLAG_ALL_TYPE 0x3f diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index 77730ce254c..92a65a50bd4 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -632,7 +632,8 @@ .falloff = 4.0f, \ .mesh_verts_num = 0, \ .bind_verts_num = 0, \ - .polys_num = 0, \ + .target_verts_num = 0, \ + .target_polys_num = 0, \ .flags = 0, \ .mat = _DNA_DEFAULT_UNIT_M4, \ .strength = 1.0f, \ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 73c4eeaaab3..6e3ce7e98a8 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -2222,13 +2222,19 @@ typedef struct SurfaceDeformModifierData { struct Object *target; /** Vertex bind data. */ SDefVert *verts; + void *_pad1; float falloff; - unsigned int mesh_verts_num, bind_verts_num, polys_num; + /* Number of of vertices on the deformed mesh upon the bind process. */ + unsigned int mesh_verts_num; + /* Number of vertices in the `verts` array of this modifier. */ + unsigned int bind_verts_num; + /* Number of vertices and polygons on the target mesh upon bind process. */ + unsigned int target_verts_num, target_polys_num; int flags; float mat[4][4]; float strength; char defgrp_name[64]; - void *_pad1; + int _pad2; } SurfaceDeformModifierData; /** Surface Deform modifier flags. */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index d18fe1b81dd..92c4d8fe938 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -855,6 +855,12 @@ typedef struct NodeVertexCol { char name[64]; } NodeVertexCol; +typedef struct NodeCMPCombSepColor { + /* CMPNodeCombSepColorMode */ + uint8_t mode; + uint8_t ycc_mode; +} NodeCMPCombSepColor; + /** Defocus blur node. */ typedef struct NodeDefocus { char bktype, _pad0, preview, gamco; @@ -1485,6 +1491,11 @@ typedef struct NodeFunctionCompare { char _pad[1]; } NodeFunctionCompare; +typedef struct NodeCombSepColor { + /* NodeCombSepColorMode */ + int8_t mode; +} NodeCombSepColor; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -1877,6 +1888,16 @@ typedef enum CMPNodeDenoisePrefilter { CMP_NODE_DENOISE_PREFILTER_ACCURATE = 2 } CMPNodeDenoisePrefilter; +/* Color combine/separate modes */ + +typedef enum CMPNodeCombSepColorMode { + CMP_NODE_COMBSEP_COLOR_RGB = 0, + CMP_NODE_COMBSEP_COLOR_HSV = 1, + CMP_NODE_COMBSEP_COLOR_HSL = 2, + CMP_NODE_COMBSEP_COLOR_YCC = 3, + CMP_NODE_COMBSEP_COLOR_YUV = 4, +} CMPNodeCombSepColorMode; + #define CMP_NODE_PLANETRACKDEFORM_MBLUR_SAMPLES_MAX 64 /* Point Density shader node */ @@ -2135,6 +2156,12 @@ typedef enum GeometryNodeScaleElementsMode { GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS = 1, } GeometryNodeScaleElementsMode; +typedef enum NodeCombSepColorMode { + NODE_COMBSEP_COLOR_RGB = 0, + NODE_COMBSEP_COLOR_HSV = 1, + NODE_COMBSEP_COLOR_HSL = 2, +} NodeCombSepColorMode; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 9ea1e3a9e8d..f257833efe8 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -535,7 +535,8 @@ enum { /** Matches #OB_TYPE_SUPPORT_EDITMODE. */ #define OB_DATA_SUPPORT_EDITMODE(_type) \ - (ELEM(_type, ID_ME, ID_CU_LEGACY, ID_MB, ID_LT, ID_AR, ID_CV)) + (ELEM(_type, ID_ME, ID_CU_LEGACY, ID_MB, ID_LT, ID_AR) || \ + (U.experimental.use_new_curves_tools && (_type) == ID_CV)) /* is this ID type used as object data */ #define OB_DATA_SUPPORT_ID(_id_type) \ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index c1e96bcfaf3..85649a31ea4 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -173,7 +173,7 @@ typedef struct SpaceProperties { /* button defines (deprecated) */ #ifdef DNA_DEPRECATED_ALLOW -/* warning: the values of these defines are used in SpaceProperties.tabs[8] */ +/* WARNING: the values of these defines are used in SpaceProperties.tabs[8] */ /* SpaceProperties.mainb new */ # define CONTEXT_SCENE 0 # define CONTEXT_OBJECT 1 @@ -1223,7 +1223,8 @@ typedef struct SpaceImage { char dt_uvstretch; char around; - char _pad1[4]; + char gizmo_flag; + char _pad1[3]; int flag; @@ -1321,6 +1322,13 @@ typedef enum eSpaceImageOverlay_Flag { SI_OVERLAY_SHOW_GRID_BACKGROUND = (1 << 1), } eSpaceImageOverlay_Flag; +/** #SpaceImage.gizmo_flag */ +enum { + /** All gizmos. */ + SI_GIZMO_HIDE = (1 << 0), + SI_GIZMO_HIDE_NAVIGATE = (1 << 1), +}; + /** Keep in sync with `STEPS_LEN` in `grid_frag.glsl`. */ #define SI_GRID_STEPS_LEN 8 @@ -1332,7 +1340,7 @@ typedef enum eSpaceImageOverlay_Flag { typedef struct SpaceText_Runtime { - /** Actual line height, scaled by dpi. */ + /** Actual line height, scaled by DPI. */ int lheight_px; /** Runtime computed, character width. */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 21abb632b94..275a89ec680 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -57,7 +57,7 @@ typedef struct uiFontStyle { /** Saved in file, 0 is default. */ short uifont_id; char _pad1[2]; - /** Actual size depends on 'global' dpi. */ + /** Actual size depends on 'global' DPI. */ float points; /** Style hint. */ short italic, bold; @@ -643,6 +643,8 @@ typedef struct UserDef_Experimental { /* The following options are automatically sanitized (set to 0) * when the release cycle is not alpha. */ char use_new_curves_type; + /** Only available when #use_new_curves_type is enabled. */ + char use_new_curves_tools; char use_new_point_cloud_type; char use_full_frame_compositor; char use_sculpt_tools_tilt; @@ -651,7 +653,6 @@ typedef struct UserDef_Experimental { char enable_eevee_next; char use_sculpt_texture_paint; char use_draw_manager_acquire_lock; - char _pad0[1]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index 86649357433..f25ff5fbbb8 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -61,6 +61,8 @@ DNA_STRUCT_RENAME_ELEM(Curve, ext1, extrude) DNA_STRUCT_RENAME_ELEM(Curve, ext2, bevel_radius) DNA_STRUCT_RENAME_ELEM(Curve, len_wchar, len_char32) DNA_STRUCT_RENAME_ELEM(Curve, width, offset) +DNA_STRUCT_RENAME_ELEM(CurvesGeometry, curve_size, curve_num) +DNA_STRUCT_RENAME_ELEM(CurvesGeometry, point_size, point_num) DNA_STRUCT_RENAME_ELEM(CustomDataExternal, filename, filepath) DNA_STRUCT_RENAME_ELEM(Editing, over_border, overlay_frame_rect) DNA_STRUCT_RENAME_ELEM(Editing, over_cfra, overlay_frame_abs) @@ -106,7 +108,7 @@ DNA_STRUCT_RENAME_ELEM(SDefBind, numverts, verts_num) DNA_STRUCT_RENAME_ELEM(SDefVert, numbinds, binds_num) DNA_STRUCT_RENAME_ELEM(SpaceSeq, overlay_type, overlay_frame_type) DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, num_mesh_verts, mesh_verts_num) -DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, numpoly, polys_num) +DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, numpoly, target_polys_num) DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, numverts, bind_verts_num) DNA_STRUCT_RENAME_ELEM(SurfaceModifierData, numverts, verts_num) DNA_STRUCT_RENAME_ELEM(Text, name, filepath) diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 12ec7262906..0673e6be2f5 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -235,7 +235,7 @@ static int preprocess_include(char *maindata, const int maindata_len); /** * Scan this file for serializable types. */ -static int convert_include(const char *filename); +static int convert_include(const char *filepath); /** * Determine how many bytes are needed for each struct. @@ -670,12 +670,12 @@ static int preprocess_include(char *maindata, const int maindata_len) return newlen; } -static void *read_file_data(const char *filename, int *r_len) +static void *read_file_data(const char *filepath, int *r_len) { #ifdef WIN32 - FILE *fp = fopen(filename, "rb"); + FILE *fp = fopen(filepath, "rb"); #else - FILE *fp = fopen(filename, "r"); + FILE *fp = fopen(filepath, "r"); #endif void *data; @@ -711,17 +711,17 @@ static void *read_file_data(const char *filename, int *r_len) return data; } -static int convert_include(const char *filename) +static int convert_include(const char *filepath) { /* read include file, skip structs with a '#' before it. * store all data in temporal arrays. */ int maindata_len; - char *maindata = read_file_data(filename, &maindata_len); + char *maindata = read_file_data(filepath, &maindata_len); char *md = maindata; if (maindata_len == -1) { - fprintf(stderr, "Can't read file %s\n", filename); + fprintf(stderr, "Can't read file %s\n", filepath); return 1; } @@ -759,7 +759,7 @@ static int convert_include(const char *filename) const int strct = add_type(md1, 0); if (strct == -1) { - fprintf(stderr, "File '%s' contains struct we can't parse \"%s\"\n", filename, md1); + fprintf(stderr, "File '%s' contains struct we can't parse \"%s\"\n", filepath, md1); return 1; } @@ -799,7 +799,7 @@ static int convert_include(const char *filename) fprintf(stderr, "File '%s' contains non white space character " "\"%c\" after identifier \"%s\"\n", - filename, + filepath, *md1, md1_prev); return 1; @@ -812,7 +812,7 @@ static int convert_include(const char *filename) const int type = add_type(md1, 0); if (type == -1) { fprintf( - stderr, "File '%s' contains struct we can't parse \"%s\"\n", filename, md1); + stderr, "File '%s' contains struct we can't parse \"%s\"\n", filepath, md1); return 1; } @@ -838,7 +838,7 @@ static int convert_include(const char *filename) if (name == -1) { fprintf(stderr, "File '%s' contains struct with name that can't be added \"%s\"\n", - filename, + filepath, md1); return 1; } @@ -861,7 +861,7 @@ static int convert_include(const char *filename) if (name == -1) { fprintf(stderr, "File '%s' contains struct with name that can't be added \"%s\"\n", - filename, + filepath, md1); return 1; } diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h index 74f8e2487a4..127c8465243 100644 --- a/source/blender/makesrna/RNA_enum_items.h +++ b/source/blender/makesrna/RNA_enum_items.h @@ -198,8 +198,10 @@ DEF_ENUM(rna_enum_context_mode_items) DEF_ENUM(rna_enum_preference_section_items) DEF_ENUM(rna_enum_attribute_type_items) +DEF_ENUM(rna_enum_color_attribute_type_items) DEF_ENUM(rna_enum_attribute_type_with_auto_items) DEF_ENUM(rna_enum_attribute_domain_items) +DEF_ENUM(rna_enum_color_attribute_domain_items) DEF_ENUM(rna_enum_attribute_domain_without_corner_items) DEF_ENUM(rna_enum_attribute_domain_with_auto_items) DEF_ENUM(rna_enum_geometry_component_type_items) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 8f7660d4015..53fa7a3d923 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -6723,23 +6723,27 @@ static void *rna_array_as_string_alloc( int type, int len, PointerRNA *ptr, PropertyRNA *prop, void **r_buf_end) { void *buf_ret = NULL; - if (type == PROP_BOOLEAN) { - bool *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__); - RNA_property_boolean_get_array(ptr, prop, buf); - *r_buf_end = buf + len; - } - else if (type == PROP_INT) { - int *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__); - RNA_property_int_get_array(ptr, prop, buf); - *r_buf_end = buf + len; - } - else if (type == PROP_FLOAT) { - float *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__); - RNA_property_float_get_array(ptr, prop, buf); - *r_buf_end = buf + len; - } - else { - BLI_assert(0); + switch (type) { + case PROP_BOOLEAN: { + bool *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__); + RNA_property_boolean_get_array(ptr, prop, buf); + *r_buf_end = buf + len; + break; + } + case PROP_INT: { + int *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__); + RNA_property_int_get_array(ptr, prop, buf); + *r_buf_end = buf + len; + break; + } + case PROP_FLOAT: { + float *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__); + RNA_property_float_get_array(ptr, prop, buf); + *r_buf_end = buf + len; + break; + } + default: + BLI_assert_unreachable(); } return buf_ret; } @@ -6749,29 +6753,33 @@ static void rna_array_as_string_elem(int type, void **buf_p, int len, DynStr *dy /* This will print a comma separated string of the array elements from * buf start to len. We will add a comma if len == 1 to preserve tuples. */ const int end = len - 1; - if (type == PROP_BOOLEAN) { - bool *buf = *buf_p; - for (int i = 0; i < len; i++, buf++) { - BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%s, " : "%s", bool_as_py_string(*buf)); + switch (type) { + case PROP_BOOLEAN: { + bool *buf = *buf_p; + for (int i = 0; i < len; i++, buf++) { + BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%s, " : "%s", bool_as_py_string(*buf)); + } + *buf_p = buf; + break; } - *buf_p = buf; - } - else if (type == PROP_INT) { - int *buf = *buf_p; - for (int i = 0; i < len; i++, buf++) { - BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%d, " : "%d", *buf); + case PROP_INT: { + int *buf = *buf_p; + for (int i = 0; i < len; i++, buf++) { + BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%d, " : "%d", *buf); + } + *buf_p = buf; + break; } - *buf_p = buf; - } - else if (type == PROP_FLOAT) { - float *buf = *buf_p; - for (int i = 0; i < len; i++, buf++) { - BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%g, " : "%g", *buf); + case PROP_FLOAT: { + float *buf = *buf_p; + for (int i = 0; i < len; i++, buf++) { + BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%g, " : "%g", *buf); + } + *buf_p = buf; + break; } - *buf_p = buf; - } - else { - BLI_assert(0); + default: + BLI_assert_unreachable(); } } diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 5974788884e..aea4467c162 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -87,7 +87,7 @@ static ID *rna_property_override_property_real_id_owner(Main *bmain, owner_id = RNA_find_real_ID_and_path(bmain, id, &rna_path_prefix); break; default: - BLI_assert(0); + BLI_assert_unreachable(); } } @@ -354,7 +354,7 @@ static int rna_property_override_diff(Main *bmain, if (is_array_a != is_array_b) { /* Should probably never happen actually... */ - BLI_assert(0); + BLI_assert_unreachable(); return is_array_a ? 1 : -1; } @@ -400,7 +400,7 @@ static int rna_property_override_diff(Main *bmain, rna_path ? rna_path : prop_a->identifier, !prop_a->is_idprop, !prop_b->is_idprop); - BLI_assert(0); + BLI_assert_unreachable(); return 1; } @@ -491,7 +491,7 @@ static bool rna_property_override_operation_store(Main *bmain, op->rna_path, prop_local->magic == RNA_MAGIC, prop_reference->magic == RNA_MAGIC); - BLI_assert(0); + BLI_assert_unreachable(); return changed; } @@ -580,7 +580,7 @@ static bool rna_property_override_operation_apply(Main *bmain, prop_dst->identifier, prop_dst->magic == RNA_MAGIC, prop_src->magic == RNA_MAGIC); - BLI_assert(0); + BLI_assert_unreachable(); return false; } diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index d49bac15242..2d9517ce1ee 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -40,12 +40,21 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_color_attribute_type_items[] = { + {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color 32-bit floating-point values"}, + {CD_PROP_BYTE_COLOR, + "BYTE_COLOR", + 0, + "Byte Color", + "RGBA color with 8-bit positive integer values"}, + {0, NULL, 0, NULL, NULL}}; + const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = { {CD_AUTO_FROM_NAME, "AUTO", 0, "Auto", ""}, {CD_PROP_FLOAT, "FLOAT", 0, "Float", "Floating-point value"}, {CD_PROP_INT32, "INT", 0, "Integer", "32-bit integer"}, {CD_PROP_FLOAT3, "FLOAT_VECTOR", 0, "Vector", "3D vector with floating-point values"}, - {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color 32-bit floating-point values"}, + {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color with 32-bit floating-point values"}, {CD_PROP_BYTE_COLOR, "BYTE_COLOR", 0, @@ -92,6 +101,11 @@ const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_color_attribute_domain_items[] = { + {ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", ""}, + {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""}, + {0, NULL, 0, NULL, NULL}}; + #ifdef RNA_RUNTIME # include "BLI_math.h" diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 848779c49f7..4767ef2c017 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1844,6 +1844,26 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "use_automasking_stroke", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "sculpt_mode_flag", GP_SCULPT_FLAGMODE_AUTOMASK_STROKE); + RNA_def_property_ui_text(prop, "Auto-Masking Strokes", "Mask strokes below brush cursor"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "use_automasking_layer", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "sculpt_mode_flag", GP_SCULPT_FLAGMODE_AUTOMASK_LAYER); + RNA_def_property_ui_text(prop, "Auto-Masking Layer", "Mask strokes using active layer"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "use_automasking_material", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "sculpt_mode_flag", GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL); + RNA_def_property_ui_text(prop, "Auto-Masking Material", "Mask strokes using active material"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + /* Material */ prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "Material"); @@ -1931,7 +1951,12 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) prop = RNA_def_property(srna, "add_amount", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 1, INT32_MAX); - RNA_def_property_ui_text(prop, "Add Amount", "Number of curves added by the Add brush"); + RNA_def_property_ui_text(prop, "Count", "Number of curves added by the Add brush"); + + prop = RNA_def_property(srna, "points_per_curve", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 2, INT32_MAX); + RNA_def_property_ui_text( + prop, "Points per Curve", "Number of control points in a newly added curve"); prop = RNA_def_property(srna, "scale_uniform", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM); @@ -1950,6 +1975,13 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Interpolate Length", "Use length of the curves in close proximity"); + prop = RNA_def_property(srna, "interpolate_point_count", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT); + RNA_def_property_ui_text(prop, + "Interpolate Point Count", + "Use the number of points from the curves in close proximity"); + prop = RNA_def_property(srna, "interpolate_shape", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE); RNA_def_property_ui_text( diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c index 7a1a368551f..2b406125409 100644 --- a/source/blender/makesrna/intern/rna_curves.c +++ b/source/blender/makesrna/intern/rna_curves.c @@ -38,7 +38,7 @@ static Curves *rna_curves(PointerRNA *ptr) static int rna_Curves_curve_offset_data_length(PointerRNA *ptr) { const Curves *curves = rna_curves(ptr); - return curves->geometry.curve_size + 1; + return curves->geometry.curve_num + 1; } static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) @@ -47,7 +47,7 @@ static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter, rna_iterator_array_begin(iter, (void *)curves->geometry.curve_offsets, sizeof(int), - curves->geometry.curve_size + 1, + curves->geometry.curve_num + 1, false, NULL); } @@ -133,14 +133,22 @@ static void rna_Curves_update_data(struct Main *UNUSED(bmain), PointerRNA *ptr) { ID *id = ptr->owner_id; - - /* cheating way for importers to avoid slow updates */ + /* Avoid updates for importers creating curves. */ if (id->us > 0) { DEG_id_tag_update(id, 0); WM_main_add_notifier(NC_GEOM | ND_DATA, id); } } +void rna_Curves_update_draw(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + ID *id = ptr->owner_id; + /* Avoid updates for importers creating curves. */ + if (id->us > 0) { + WM_main_add_notifier(NC_GEOM | ND_DATA, id); + } +} + #else static void rna_def_curves_point(BlenderRNA *brna) @@ -222,7 +230,7 @@ static void rna_def_curves(BlenderRNA *brna) /* Point and Curve RNA API helpers. */ prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", "geometry.curve_size"); + RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", "geometry.curve_num"); RNA_def_property_struct_type(prop, "CurveSlice"); RNA_def_property_ui_text(prop, "Curves", "All curves in the data-block"); @@ -230,7 +238,7 @@ static void rna_def_curves(BlenderRNA *brna) RNA_define_verify_sdna(0); prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_size"); + RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_num"); RNA_def_property_struct_type(prop, "CurvePoint"); RNA_def_property_ui_text(prop, "Points", "Control points of all curves"); RNA_define_verify_sdna(1); @@ -239,7 +247,7 @@ static void rna_def_curves(BlenderRNA *brna) RNA_define_verify_sdna(0); prop = RNA_def_property(srna, "position_data", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_size"); + RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_num"); RNA_def_property_struct_type(prop, "FloatVectorAttributeValue"); RNA_def_property_update(prop, 0, "rna_Curves_update_data"); RNA_define_verify_sdna(1); @@ -274,6 +282,22 @@ static void rna_def_curves(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Surface", "Mesh object that the curves can be attached to"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + /* Symmetry. */ + prop = RNA_def_property(srna, "use_mirror_x", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "symmetry", CURVES_SYMMETRY_X); + RNA_def_property_ui_text(prop, "X", "Enable symmetry in the X axis"); + RNA_def_property_update(prop, 0, "rna_Curves_update_draw"); + + prop = RNA_def_property(srna, "use_mirror_y", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "symmetry", CURVES_SYMMETRY_Y); + RNA_def_property_ui_text(prop, "Y", "Enable symmetry in the Y axis"); + RNA_def_property_update(prop, 0, "rna_Curves_update_draw"); + + prop = RNA_def_property(srna, "use_mirror_z", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "symmetry", CURVES_SYMMETRY_Z); + RNA_def_property_ui_text(prop, "Z", "Enable symmetry in the Z axis"); + RNA_def_property_update(prop, 0, "rna_Curves_update_draw"); + /* attributes */ rna_def_attributes_common(srna); diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 8e2b9c1d937..1e950b883ab 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -208,7 +208,7 @@ static int DNA_struct_find_nr_wrapper(const struct SDNA *sdna, const char *struc struct_name = DNA_struct_rename_legacy_hack_static_from_alias(struct_name); #ifdef RNA_RUNTIME /* We may support this at some point but for now we don't. */ - BLI_assert(0); + BLI_assert_unreachable(); #else struct_name = BLI_ghash_lookup_default( g_version_data.struct_map_static_from_alias, struct_name, (void *)struct_name); diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 1f9bb972923..e6cb78ffcfc 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -232,6 +232,11 @@ static const EnumPropertyItem gpencil_envelope_mode_items[] = { "Add fill segments to create the envelope. Don't keep the original stroke"}, {0, NULL, 0, NULL, NULL}, }; +static const EnumPropertyItem modifier_noise_random_mode_items[] = { + {GP_NOISE_RANDOM_STEP, "STEP", 0, "Steps", "Randomize every number of frames"}, + {GP_NOISE_RANDOM_KEYFRAME, "KEYFRAME", 0, "Keyframes", "Randomize on keyframes only"}, + {0, NULL, 0, NULL, NULL}, +}; #endif #ifdef RNA_RUNTIME @@ -924,8 +929,7 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) prop = RNA_def_property(srna, "step", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "step"); RNA_def_property_range(prop, 1, 100); - RNA_def_property_ui_text( - prop, "Step", "Number of frames before recalculate random values again"); + RNA_def_property_ui_text(prop, "Step", "Number of frames between randomization steps"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE); @@ -959,6 +963,12 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "random_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "noise_mode"); + RNA_def_property_enum_items(prop, modifier_noise_random_mode_items); + RNA_def_property_ui_text(prop, "Mode", "Where to perform randomization"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + RNA_define_lib_overridable(false); } @@ -3259,12 +3269,6 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 30.0f); RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "use_remove_doubles", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_REMOVE_DOUBLES); - RNA_def_property_ui_text( - prop, "Remove Doubles", "Remove doubles from the source geometry before generating stokes"); - RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "use_loose_as_contour", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_LOOSE_AS_CONTOUR); RNA_def_property_ui_text(prop, "Loose As Contour", "Loose edges will have contour type"); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index bd3b03add95..81708ac8e65 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -737,6 +737,16 @@ static void rna_def_image_packed_files(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "filepath"); RNA_def_struct_name_property(srna, prop); + prop = RNA_def_property(srna, "view", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "view"); + RNA_def_property_ui_text(prop, "View Index", ""); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + prop = RNA_def_property(srna, "tile_number", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "tile_number"); + RNA_def_property_ui_text(prop, "Tile Number", ""); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_api_image_packed_file(srna); } diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index 897573f9fd9..b385f576585 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -26,6 +26,7 @@ # include "BKE_image.h" # include "BKE_image_format.h" +# include "BKE_image_save.h" # include "BKE_main.h" # include "BKE_scene.h" # include <errno.h> @@ -50,77 +51,40 @@ static void rna_ImagePackedFile_save(ImagePackedFile *imapf, Main *bmain, Report static void rna_Image_save_render( Image *image, bContext *C, ReportList *reports, const char *path, Scene *scene) { - ImBuf *ibuf; + Main *bmain = CTX_data_main(C); if (scene == NULL) { scene = CTX_data_scene(C); } - if (scene) { - ImageUser iuser = {NULL}; - void *lock; + ImageSaveOptions opts; - iuser.scene = scene; + if (BKE_image_save_options_init(&opts, bmain, scene, image, NULL, false)) { + opts.save_copy = true; + opts.save_as_render = true; + STRNCPY(opts.filepath, path); - ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); - - if (ibuf == NULL) { - BKE_report(reports, RPT_ERROR, "Could not acquire buffer from image"); - } - else { - ImBuf *write_ibuf; - - ImageFormatData image_format; - BKE_image_format_init_for_write(&image_format, scene, NULL); - - write_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, true, true, &image_format); - - write_ibuf->planes = image_format.planes; - write_ibuf->dither = scene->r.dither_intensity; - - if (!BKE_imbuf_write(write_ibuf, path, &image_format)) { - BKE_reportf(reports, RPT_ERROR, "Could not write image: %s, '%s'", strerror(errno), path); - } - - if (write_ibuf != ibuf) { - IMB_freeImBuf(write_ibuf); - } - - BKE_image_format_free(&image_format); + if (!BKE_image_save(reports, bmain, image, NULL, &opts)) { + BKE_reportf( + reports, RPT_ERROR, "Image '%s' could not be saved to '%s'", image->id.name + 2, path); } - - BKE_image_release_ibuf(image, ibuf, lock); } else { - BKE_report(reports, RPT_ERROR, "Scene not in context, could not get save parameters"); + BKE_reportf(reports, RPT_ERROR, "Image '%s' does not have any image data", image->id.name + 2); } + + BKE_image_save_options_free(&opts); + + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, image); } static void rna_Image_save(Image *image, Main *bmain, bContext *C, ReportList *reports) { - void *lock; - - ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); - if (ibuf) { - char filepath[FILE_MAX]; - BLI_strncpy(filepath, image->filepath, sizeof(filepath)); - BLI_path_abs(filepath, ID_BLEND_PATH(bmain, &image->id)); - - /* NOTE: we purposefully ignore packed files here, - * developers need to explicitly write them via 'packed_files' */ + Scene *scene = CTX_data_scene(C); + ImageSaveOptions opts; - if (IMB_saveiff(ibuf, filepath, ibuf->flags)) { - image->type = IMA_TYPE_IMAGE; - - if (image->source == IMA_SRC_GENERATED) { - image->source = IMA_SRC_FILE; - } - - IMB_colormanagement_colorspace_from_ibuf_ftype(&image->colorspace_settings, ibuf); - - ibuf->userflags &= ~IB_BITMAPDIRTY; - } - else { + if (BKE_image_save_options_init(&opts, bmain, scene, image, NULL, false)) { + if (!BKE_image_save(reports, bmain, image, NULL, &opts)) { BKE_reportf(reports, RPT_ERROR, "Image '%s' could not be saved to '%s'", @@ -132,7 +96,8 @@ static void rna_Image_save(Image *image, Main *bmain, bContext *C, ReportList *r BKE_reportf(reports, RPT_ERROR, "Image '%s' does not have any image data", image->id.name + 2); } - BKE_image_release_ibuf(image, ibuf, lock); + BKE_image_save_options_free(&opts); + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, image); } @@ -161,9 +126,8 @@ static void rna_Image_unpack(Image *image, Main *bmain, ReportList *reports, int if (!BKE_image_has_packedfile(image)) { BKE_report(reports, RPT_ERROR, "Image not packed"); } - else if (BKE_image_has_multiple_ibufs(image)) { - BKE_report( - reports, RPT_ERROR, "Unpacking movies, image sequences or tiled images not supported"); + else if (ELEM(image->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) { + BKE_report(reports, RPT_ERROR, "Unpacking movies or image sequences not supported"); return; } else { diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 38d473f04dd..2ba3cf1096c 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -560,7 +560,7 @@ static void rna_MeshVertex_undeformed_co_get(PointerRNA *ptr, float values[3]) { Mesh *me = rna_mesh(ptr); MVert *mvert = (MVert *)ptr->data; - float(*orco)[3] = CustomData_get_layer(&me->vdata, CD_ORCO); + const float(*orco)[3] = CustomData_get_layer(&me->vdata, CD_ORCO); if (orco) { /* orco is normalized to 0..1, we do inverse to match mvert->co */ @@ -626,9 +626,10 @@ static void rna_CustomDataLayer_clone_set(PointerRNA *ptr, CustomData *data, int static bool rna_MEdge_freestyle_edge_mark_get(PointerRNA *ptr) { - Mesh *me = rna_mesh(ptr); - MEdge *medge = (MEdge *)ptr->data; - FreestyleEdge *fed = CustomData_get(&me->edata, (int)(medge - me->medge), CD_FREESTYLE_EDGE); + const Mesh *me = rna_mesh(ptr); + const MEdge *medge = (MEdge *)ptr->data; + const FreestyleEdge *fed = CustomData_get( + &me->edata, (int)(medge - me->medge), CD_FREESTYLE_EDGE); return fed && (fed->flag & FREESTYLE_EDGE_MARK) != 0; } @@ -652,9 +653,10 @@ static void rna_MEdge_freestyle_edge_mark_set(PointerRNA *ptr, bool value) static bool rna_MPoly_freestyle_face_mark_get(PointerRNA *ptr) { - Mesh *me = rna_mesh(ptr); - MPoly *mpoly = (MPoly *)ptr->data; - FreestyleFace *ffa = CustomData_get(&me->pdata, (int)(mpoly - me->mpoly), CD_FREESTYLE_FACE); + const Mesh *me = rna_mesh(ptr); + const MPoly *mpoly = (MPoly *)ptr->data; + const FreestyleFace *ffa = CustomData_get( + &me->pdata, (int)(mpoly - me->mpoly), CD_FREESTYLE_FACE); return ffa && (ffa->flag & FREESTYLE_FACE_MARK) != 0; } diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 456f774648a..6dd7fe53774 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -130,7 +130,7 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = { ICON_MOD_EDGESPLIT, "Edge Split", "Split away joined faces at the edges"}, - {eModifierType_Nodes, "NODES", ICON_NODETREE, "Geometry Nodes", ""}, + {eModifierType_Nodes, "NODES", ICON_GEOMETRY_NODES, "Geometry Nodes", ""}, {eModifierType_Mask, "MASK", ICON_MOD_MASK, @@ -6991,7 +6991,7 @@ static void rna_def_modifier_nodes(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Nodes Modifier", ""); RNA_def_struct_sdna(srna, "NodesModifierData"); RNA_def_struct_idprops_func(srna, "rna_NodesModifier_properties"); - RNA_def_struct_ui_icon(srna, ICON_NODETREE); + RNA_def_struct_ui_icon(srna, ICON_GEOMETRY_NODES); RNA_define_lib_overridable(true); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index eaf2142495e..9b9afadbd05 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -489,6 +489,13 @@ static const EnumPropertyItem rna_node_geometry_curve_handle_side_items[] = { {GEO_NODE_CURVE_HANDLE_RIGHT, "RIGHT", ICON_NONE, "Right", "Use the right handles"}, {0, NULL, 0, NULL, NULL}}; +static const EnumPropertyItem rna_node_combsep_color_items[] = { + {NODE_COMBSEP_COLOR_RGB, "RGB", ICON_NONE, "RGB", "Use RGB color processing"}, + {NODE_COMBSEP_COLOR_HSV, "HSV", ICON_NONE, "HSV", "Use HSV color processing"}, + {NODE_COMBSEP_COLOR_HSL, "HSL", ICON_NONE, "HSL", "Use HSL color processing"}, + {0, NULL, 0, NULL, NULL}, +}; + #ifndef RNA_RUNTIME static const EnumPropertyItem node_sampler_type_items[] = { {0, "NEAREST", 0, "Nearest", ""}, @@ -5050,6 +5057,18 @@ static void def_fn_input_string(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_fn_combsep_color(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeCombSepColor", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_combsep_color_items); + RNA_def_property_ui_text(prop, "Mode", "Mode of color processing"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + /* -- Shader Nodes ---------------------------------------------------------- */ static void def_sh_output(StructRNA *srna) @@ -5401,6 +5420,17 @@ static void def_sh_tex_image(StructRNA *srna) RNA_def_property_update(prop, 0, "rna_Node_update"); } +static void def_tex_combsep_color(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, rna_node_combsep_color_items); + RNA_def_property_ui_text(prop, "Mode", "Mode of color processing"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_image_texture(StructRNA *srna) { static const EnumPropertyItem fn_tex_prop_interpolation_items[] = { @@ -6308,6 +6338,25 @@ static void def_sh_output_aov(StructRNA *srna) RNA_def_struct_sdna_from(srna, "bNode", NULL); } +static void def_sh_combsep_color(StructRNA *srna) +{ + static const EnumPropertyItem type_items[] = { + {NODE_COMBSEP_COLOR_RGB, "RGB", ICON_NONE, "RGB", "Use RGB color processing"}, + {NODE_COMBSEP_COLOR_HSV, "HSV", ICON_NONE, "HSV", "Use HSV color processing"}, + {NODE_COMBSEP_COLOR_HSL, "HSL", ICON_NONE, "HSL", "Use HSL color processing"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeCombSepColor", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, type_items); + RNA_def_property_ui_text(prop, "Mode", "Mode of color processing"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_sh_script(StructRNA *srna) { PropertyRNA *prop; @@ -8071,6 +8120,32 @@ static void def_cmp_ycc(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_cmp_combsep_color(StructRNA *srna) +{ + static const EnumPropertyItem mode_items[] = { + {CMP_NODE_COMBSEP_COLOR_RGB, "RGB", ICON_NONE, "RGB", "Use RGB color processing"}, + {CMP_NODE_COMBSEP_COLOR_HSV, "HSV", ICON_NONE, "HSV", "Use HSV color processing"}, + {CMP_NODE_COMBSEP_COLOR_HSL, "HSL", ICON_NONE, "HSL", "Use HSL color processing"}, + {CMP_NODE_COMBSEP_COLOR_YCC, "YCC", ICON_NONE, "YCbCr", "Use YCbCr color processing"}, + {CMP_NODE_COMBSEP_COLOR_YUV, "YUV", ICON_NONE, "YUV", "Use YUV color processing"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeCMPCombSepColor", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "Mode of color processing"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "ycc_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, node_ycc_items); + RNA_def_property_ui_text(prop, "Color Space", "Color space used for YCbCrA processing"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_cmp_movieclip(StructRNA *srna) { PropertyRNA *prop; @@ -12294,7 +12369,7 @@ static void rna_def_nodetree(BlenderRNA *brna) {NTREE_SHADER, "SHADER", ICON_MATERIAL, "Shader", "Shader nodes"}, {NTREE_TEXTURE, "TEXTURE", ICON_TEXTURE, "Texture", "Texture nodes"}, {NTREE_COMPOSIT, "COMPOSITING", ICON_RENDERLAYERS, "Compositing", "Compositing nodes"}, - {NTREE_GEOMETRY, "GEOMETRY", ICON_NODETREE, "Geometry", "Geometry nodes"}, + {NTREE_GEOMETRY, "GEOMETRY", ICON_GEOMETRY_NODES, "Geometry", "Geometry nodes"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index f92ea8df459..addc8ac0c6c 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -1395,97 +1395,97 @@ static void rna_def_field(BlenderRNA *brna) PropertyRNA *prop; static const EnumPropertyItem field_type_items[] = { - {0, "NONE", 0, "None", ""}, + {0, "NONE", ICON_BLANK1, "None", ""}, + {PFIELD_BOID, + "BOID", + ICON_FORCE_BOID, + "Boid", + "Create a force that acts as a boid's predators or target"}, + {PFIELD_CHARGE, + "CHARGE", + ICON_FORCE_CHARGE, + "Charge", + "Spherical forcefield based on the charge of particles, " + "only influences other charge force fields"}, + {PFIELD_GUIDE, + "GUIDE", + ICON_FORCE_CURVE, + "Curve Guide", + "Create a force along a curve object"}, + {PFIELD_DRAG, "DRAG", ICON_FORCE_DRAG, "Drag", "Create a force that dampens motion"}, + {PFIELD_FLUIDFLOW, + "FLUID_FLOW", + ICON_FORCE_FLUIDFLOW, + "Fluid Flow", + "Create a force based on fluid simulation velocities"}, {PFIELD_FORCE, "FORCE", ICON_FORCE_FORCE, "Force", "Radial field toward the center of object"}, - {PFIELD_WIND, - "WIND", - ICON_FORCE_WIND, - "Wind", - "Constant force along the force object's local Z axis"}, - {PFIELD_VORTEX, - "VORTEX", - ICON_FORCE_VORTEX, - "Vortex", - "Spiraling force that twists the force object's local Z axis"}, - {PFIELD_MAGNET, - "MAGNET", - ICON_FORCE_MAGNETIC, - "Magnetic", - "Forcefield depends on the speed of the particles"}, {PFIELD_HARMONIC, "HARMONIC", ICON_FORCE_HARMONIC, "Harmonic", "The source of this force field is the zero point of a harmonic oscillator"}, - {PFIELD_CHARGE, - "CHARGE", - ICON_FORCE_CHARGE, - "Charge", - "Spherical forcefield based on the charge of particles, " - "only influences other charge force fields"}, {PFIELD_LENNARDJ, "LENNARDJ", ICON_FORCE_LENNARDJONES, "Lennard-Jones", "Forcefield based on the Lennard-Jones potential"}, + {PFIELD_MAGNET, + "MAGNET", + ICON_FORCE_MAGNETIC, + "Magnetic", + "Forcefield depends on the speed of the particles"}, {PFIELD_TEXTURE, "TEXTURE", ICON_FORCE_TEXTURE, "Texture", "Force field based on a texture"}, - {PFIELD_GUIDE, - "GUIDE", - ICON_FORCE_CURVE, - "Curve Guide", - "Create a force along a curve object"}, - {PFIELD_BOID, - "BOID", - ICON_FORCE_BOID, - "Boid", - "Create a force that acts as a boid's predators or target"}, {PFIELD_TURBULENCE, "TURBULENCE", ICON_FORCE_TURBULENCE, "Turbulence", "Create turbulence with a noise field"}, - {PFIELD_DRAG, "DRAG", ICON_FORCE_DRAG, "Drag", "Create a force that dampens motion"}, - {PFIELD_FLUIDFLOW, - "FLUID_FLOW", - ICON_FORCE_FLUIDFLOW, - "Fluid Flow", - "Create a force based on fluid simulation velocities"}, + {PFIELD_VORTEX, + "VORTEX", + ICON_FORCE_VORTEX, + "Vortex", + "Spiraling force that twists the force object's local Z axis"}, + {PFIELD_WIND, + "WIND", + ICON_FORCE_WIND, + "Wind", + "Constant force along the force object's local Z axis"}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem falloff_items[] = { + {PFIELD_FALL_CONE, "CONE", 0, "Cone", ""}, {PFIELD_FALL_SPHERE, "SPHERE", 0, "Sphere", ""}, {PFIELD_FALL_TUBE, "TUBE", 0, "Tube", ""}, - {PFIELD_FALL_CONE, "CONE", 0, "Cone", ""}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem texture_items[] = { - {PFIELD_TEX_RGB, "RGB", 0, "RGB", ""}, - {PFIELD_TEX_GRAD, "GRADIENT", 0, "Gradient", ""}, {PFIELD_TEX_CURL, "CURL", 0, "Curl", ""}, + {PFIELD_TEX_GRAD, "GRADIENT", 0, "Gradient", ""}, + {PFIELD_TEX_RGB, "RGB", 0, "RGB", ""}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem zdirection_items[] = { - {PFIELD_Z_BOTH, "BOTH", 0, "Both Z", ""}, {PFIELD_Z_POS, "POSITIVE", 0, "+Z", ""}, {PFIELD_Z_NEG, "NEGATIVE", 0, "-Z", ""}, + {PFIELD_Z_BOTH, "BOTH", 0, "Both Z", ""}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem guide_kink_items[] = { - {0, "NONE", 0, "Nothing", ""}, + {0, "NONE", 0, "None", ""}, + {4, "BRAID", 0, "Braid", ""}, {1, "CURL", 0, "Curl", ""}, {2, "RADIAL", 0, "Radial", ""}, - {3, "WAVE", 0, "Wave", ""}, - {4, "BRAID", 0, "Braid", ""}, - {5, "ROTATION", 0, "Rotation", ""}, {6, "ROLL", 0, "Roll", ""}, + {5, "ROTATION", 0, "Rotation", ""}, + {3, "WAVE", 0, "Wave", ""}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index 6f2264680c8..8a024fdcfc2 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -660,7 +660,7 @@ static void rna_ParticleSystem_uv_on_emitter(ParticleSystem *particlesystem, } else { MFace *mface = &modifier->mesh_final->mface[num]; - MTFace *mtface = (MTFace *)CustomData_get_layer_n( + const MTFace *mtface = (const MTFace *)CustomData_get_layer_n( &modifier->mesh_final->fdata, CD_MTFACE, uv_no); psys_interpolate_uvs(&mtface[num], mface->v4, *fuv, r_uv); @@ -694,7 +694,8 @@ static void rna_ParticleSystem_mcol_on_emitter(ParticleSystem *particlesystem, } else { MFace *mface = &modifier->mesh_final->mface[num]; - MCol *mc = (MCol *)CustomData_get_layer_n(&modifier->mesh_final->fdata, CD_MCOL, vcol_no); + const MCol *mc = (const MCol *)CustomData_get_layer_n( + &modifier->mesh_final->fdata, CD_MCOL, vcol_no); MCol mcol; psys_interpolate_mcol(&mc[num * 4], mface->v4, *fuv, &mcol); diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index a7d673d3fe1..d540b51a585 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -2655,7 +2655,7 @@ bool rna_property_override_apply_default(Main *bmain, break; } default: - BLI_assert(0); + BLI_assert_unreachable(); return false; } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index b647a823929..63a68d04a97 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2085,7 +2085,7 @@ static char *rna_TransformOrientationSlot_path(PointerRNA *ptr) } /* Should not happen, but in case, just return default path. */ - BLI_assert(0); + BLI_assert_unreachable(); return BLI_strdup("transform_orientation_slots[0]"); } diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 51f62b62c8e..3f371188578 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -528,7 +528,7 @@ static char *rna_SequenceTransform_path(PointerRNA *ptr) Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_transform(ed, ptr->data); - if (seq && seq->name + 2) { + if (seq) { char name_esc[(sizeof(seq->name) - 2) * 2]; BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc)); @@ -580,7 +580,7 @@ static char *rna_SequenceCrop_path(PointerRNA *ptr) Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_crop(ed, ptr->data); - if (seq && seq->name + 2) { + if (seq) { char name_esc[(sizeof(seq->name) - 2) * 2]; BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc)); @@ -725,15 +725,10 @@ static char *rna_Sequence_path(PointerRNA *ptr) /* sequencer data comes from scene... * TODO: would be nice to make SequenceEditor data a data-block of its own (for shorter paths) */ - if (seq->name + 2) { - char name_esc[(sizeof(seq->name) - 2) * 2]; + char name_esc[(sizeof(seq->name) - 2) * 2]; - BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc)); - return BLI_sprintfN("sequence_editor.sequences_all[\"%s\"]", name_esc); - } - else { - return BLI_strdup(""); - } + BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc)); + return BLI_sprintfN("sequence_editor.sequences_all[\"%s\"]", name_esc); } static IDProperty **rna_Sequence_idprops(PointerRNA *ptr) @@ -1064,7 +1059,7 @@ static char *rna_SequenceColorBalance_path(PointerRNA *ptr) Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_colorbalance(ed, ptr->data, &smd); - if (seq && seq->name + 2) { + if (seq) { char name_esc[(sizeof(seq->name) - 2) * 2]; BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc)); @@ -1208,7 +1203,7 @@ static char *rna_SequenceModifier_path(PointerRNA *ptr) SequenceModifierData *smd = ptr->data; Sequence *seq = sequence_get_by_modifier(ed, smd); - if (seq && seq->name + 2) { + if (seq) { char name_esc[(sizeof(seq->name) - 2) * 2]; char name_esc_smd[sizeof(smd->name) * 2]; diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 1a9e7f5aad8..dda467b06b8 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3292,7 +3292,7 @@ static struct IDFilterEnumPropertyItem rna_enum_space_file_id_filter_categories[ {FILTER_ID_AR | FILTER_ID_CU_LEGACY | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO, "category_geometry", - ICON_NODETREE, + ICON_GEOMETRY_NODES, "Geometry", "Show meshes, curves, lattice, armatures and metaballs data"}, {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE, @@ -5407,6 +5407,17 @@ static void rna_def_space_image(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Show Mask Editor", "Show Mask editing related properties"); + /* Gizmo Toggles. */ + prop = RNA_def_property(srna, "show_gizmo", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "gizmo_flag", SI_GIZMO_HIDE); + RNA_def_property_ui_text(prop, "Show Gizmo", "Show gizmos of all types"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + + prop = RNA_def_property(srna, "show_gizmo_navigate", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "gizmo_flag", SI_GIZMO_HIDE_NAVIGATE); + RNA_def_property_ui_text(prop, "Navigate Gizmo", "Viewport navigation gizmo"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + /* Overlays */ prop = RNA_def_property(srna, "overlay", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c index e604469e3fd..aac84f1867e 100644 --- a/source/blender/makesrna/intern/rna_tracking.c +++ b/source/blender/makesrna/intern/rna_tracking.c @@ -557,7 +557,7 @@ static void rna_tracking_markerPattern_update(Main *UNUSED(bmain), { MovieTrackingMarker *marker = (MovieTrackingMarker *)ptr->data; - BKE_tracking_marker_clamp(marker, CLAMP_PAT_DIM); + BKE_tracking_marker_clamp_search_size(marker); } static void rna_tracking_markerSearch_update(Main *UNUSED(bmain), @@ -566,7 +566,7 @@ static void rna_tracking_markerSearch_update(Main *UNUSED(bmain), { MovieTrackingMarker *marker = (MovieTrackingMarker *)ptr->data; - BKE_tracking_marker_clamp(marker, CLAMP_SEARCH_DIM); + BKE_tracking_marker_clamp_search_size(marker); } static void rna_tracking_markerPattern_boundbox_get(PointerRNA *ptr, float *values) diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 6f57e2755ee..b3d4ae80713 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -1101,6 +1101,16 @@ int rna_show_statusbar_vram_editable(struct PointerRNA *UNUSED(ptr), const char return GPU_mem_stats_supported() ? PROP_EDITABLE : 0; } +static int rna_userdef_experimental_use_new_curve_tools_editable(struct PointerRNA *UNUSED(ptr), + const char **r_info) +{ + if (U.experimental.use_new_curves_type) { + return PROP_EDITABLE; + } + *r_info = "Only available when new curves type is enabled"; + return 0; +} + #else # define USERDEF_TAG_DIRTY_PROPERTY_UPDATE_ENABLE \ @@ -6394,6 +6404,12 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "use_new_curves_type", 1); RNA_def_property_ui_text(prop, "New Curves Type", "Enable the new curves data type in the UI"); + prop = RNA_def_property(srna, "use_new_curves_tools", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_new_curves_tools", 1); + RNA_def_property_editable_func(prop, "rna_userdef_experimental_use_new_curve_tools_editable"); + RNA_def_property_ui_text( + prop, "New Curves Tools", "Enable additional features for the new curves data block"); + prop = RNA_def_property(srna, "use_cycles_debug", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_cycles_debug", 1); RNA_def_property_ui_text(prop, "Cycles Debug", "Enable Cycles debugging options for developers"); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index b09a9ab0733..0ac6dc7aaa9 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -896,8 +896,10 @@ static void rna_wmKeyMapItem_map_type_set(PointerRNA *ptr, int value) } } -/* assumes value to be an enum from rna_enum_event_type_items */ -/* function makes sure keymodifiers are only valid keys, ESC keeps it unaltered */ +/** + * Assumes value to be an enum from rna_enum_event_type_items. + * Function makes sure keymodifiers are only valid keys, ESC keeps it unaltered. + */ static void rna_wmKeyMapItem_keymodifier_set(PointerRNA *ptr, int value) { wmKeyMapItem *kmi = ptr->data; diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index a04b29b8815..696d2d0f31d 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -1196,6 +1196,50 @@ static int rna_XrEventData_action_length(PointerRNA *ptr) # endif } +static void rna_XrEventData_user_path_get(PointerRNA *ptr, char *r_value) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + strcpy(r_value, data->user_path); +# else + UNUSED_VARS(ptr); + r_value[0] = '\0'; +# endif +} + +static int rna_XrEventData_user_path_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return strlen(data->user_path); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + +static void rna_XrEventData_user_path_other_get(PointerRNA *ptr, char *r_value) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + strcpy(r_value, data->user_path_other); +# else + UNUSED_VARS(ptr); + r_value[0] = '\0'; +# endif +} + +static int rna_XrEventData_user_path_other_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return strlen(data->user_path_other); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + static int rna_XrEventData_type_get(PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -2402,6 +2446,19 @@ static void rna_def_xr_eventdata(BlenderRNA *brna) prop, "rna_XrEventData_action_get", "rna_XrEventData_action_length", NULL); RNA_def_property_ui_text(prop, "Action", "XR action name"); + prop = RNA_def_property(srna, "user_path", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_funcs( + prop, "rna_XrEventData_user_path_get", "rna_XrEventData_user_path_length", NULL); + RNA_def_property_ui_text(prop, "User Path", "User path of the action. E.g. \"/user/hand/left\""); + + prop = RNA_def_property(srna, "user_path_other", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_funcs( + prop, "rna_XrEventData_user_path_other_get", "rna_XrEventData_user_path_other_length", NULL); + RNA_def_property_ui_text( + prop, "User Path Other", "Other user path, for bimanual actions. E.g. \"/user/hand/right\""); + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_enum_items(prop, rna_enum_xr_action_types); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index a5e5bf36dcd..1aac3c2191d 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -70,7 +70,7 @@ set(SRC intern/MOD_normal_edit.c intern/MOD_ocean.c intern/MOD_particleinstance.c - intern/MOD_particlesystem.c + intern/MOD_particlesystem.cc intern/MOD_remesh.c intern/MOD_screw.c intern/MOD_shapekey.c diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index a7364af10a3..0b2ea0e1ade 100644 --- a/source/blender/modifiers/intern/MOD_bevel.c +++ b/source/blender/modifiers/intern/MOD_bevel.c @@ -396,10 +396,12 @@ static void panelRegister(ARegionType *region_type) region_type, "shading", "Shading", NULL, shading_panel_draw, panel_type); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) { const BevelModifierData *bmd = (const BevelModifierData *)md; + BLO_write_struct(writer, BevelModifierData, bmd); + if (bmd->custom_profile) { BKE_curveprofile_blend_write(writer, bmd->custom_profile); } diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c index 52162eaacc5..8b6c306dae8 100644 --- a/source/blender/modifiers/intern/MOD_correctivesmooth.c +++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c @@ -798,12 +798,25 @@ static void panelRegister(ARegionType *region_type) modifier_panel_register(region_type, eModifierType_CorrectiveSmooth, panel_draw); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *id_owner, const ModifierData *md) { - const CorrectiveSmoothModifierData *csmd = (const CorrectiveSmoothModifierData *)md; + CorrectiveSmoothModifierData csmd = *(const CorrectiveSmoothModifierData *)md; + + if (ID_IS_OVERRIDE_LIBRARY(id_owner)) { + BLI_assert(!ID_IS_LINKED(id_owner)); + const bool is_local = (md->flag & eModifierFlag_OverrideLibrary_Local) != 0; + if (!is_local) { + /* Modifier coming from linked data cannot be bound from an override, so we can remove all + * binding data, can save a significant amount of memory. */ + csmd.bind_coords_num = 0; + csmd.bind_coords = NULL; + } + } - if (csmd->bind_coords) { - BLO_write_float3_array(writer, csmd->bind_coords_num, (float *)csmd->bind_coords); + BLO_write_struct_at_address(writer, CorrectiveSmoothModifierData, md, &csmd); + + if (csmd.bind_coords != NULL) { + BLO_write_float3_array(writer, csmd.bind_coords_num, (float *)csmd.bind_coords); } } diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index 7ad7d6eef3d..149cf0c0cbb 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -310,13 +310,11 @@ static void displaceModifier_do(DisplaceModifierData *dmd, CustomData *ldata = &mesh->ldata; if (CustomData_has_layer(ldata, CD_CUSTOMLOOPNORMAL)) { - float(*clnors)[3] = NULL; - if (!CustomData_has_layer(ldata, CD_NORMAL)) { BKE_mesh_calc_normals_split(mesh); } - clnors = CustomData_get_layer(ldata, CD_NORMAL); + float(*clnors)[3] = CustomData_get_layer(ldata, CD_NORMAL); vert_clnors = MEM_malloc_arrayN(verts_num, sizeof(*vert_clnors), __func__); BKE_mesh_normals_loop_to_vertex( verts_num, mesh->mloop, mesh->totloop, (const float(*)[3])clnors, vert_clnors); diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c index 9e2bb79138e..d76a750f7e8 100644 --- a/source/blender/modifiers/intern/MOD_explode.c +++ b/source/blender/modifiers/intern/MOD_explode.c @@ -124,7 +124,7 @@ static void createFacepa(ExplodeModifierData *emd, ParticleSystemModifierData *p /* set protected verts */ if (emd->vgroup) { - MDeformVert *dvert = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); + const MDeformVert *dvert = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); if (dvert) { const int defgrp_index = emd->vgroup - 1; for (i = 0; i < totvert; i++, dvert++) { @@ -911,7 +911,6 @@ static Mesh *explodeMesh(ExplodeModifierData *emd, int totdup = 0, totvert = 0, totface = 0, totpart = 0, delface = 0; int i, v, u; uint ed_v1, ed_v2, mindex = 0; - MTFace *mtface = NULL, *mtf; totface = mesh->totface; totvert = mesh->totvert; @@ -977,7 +976,7 @@ static Mesh *explodeMesh(ExplodeModifierData *emd, /* the final duplicated vertices */ explode = BKE_mesh_new_nomain_from_template(mesh, totdup, 0, totface - delface, 0, 0); - mtface = CustomData_get_layer_named(&explode->fdata, CD_MTFACE, emd->uvname); + MTFace *mtface = CustomData_get_layer_named(&explode->fdata, CD_MTFACE, emd->uvname); /* getting back to object space */ invert_m4_m4(imat, ctx->object->obmat); @@ -1086,7 +1085,7 @@ static Mesh *explodeMesh(ExplodeModifierData *emd, /* Clamp to this range to avoid flipping to the other side of the coordinates. */ CLAMP(age, 0.001f, 0.999f); - mtf = mtface + u; + MTFace *mtf = mtface + u; mtf->uv[0][0] = mtf->uv[1][0] = mtf->uv[2][0] = mtf->uv[3][0] = age; mtf->uv[0][1] = mtf->uv[1][1] = mtf->uv[2][1] = mtf->uv[3][1] = 0.5f; diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c index 1000bbf45d6..3649ece12e1 100644 --- a/source/blender/modifiers/intern/MOD_hook.c +++ b/source/blender/modifiers/intern/MOD_hook.c @@ -519,10 +519,12 @@ static void panelRegister(ARegionType *region_type) region_type, "falloff", "Falloff", NULL, falloff_panel_draw, panel_type); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) { const HookModifierData *hmd = (const HookModifierData *)md; + BLO_write_struct(writer, HookModifierData, hmd); + if (hmd->curfalloff) { BKE_curvemapping_blend_write(writer, hmd->curfalloff); } diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c index 239cb7f5a5a..06ded1c4488 100644 --- a/source/blender/modifiers/intern/MOD_laplaciandeform.c +++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c @@ -843,11 +843,26 @@ static void panelRegister(ARegionType *region_type) modifier_panel_register(region_type, eModifierType_LaplacianDeform, panel_draw); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *id_owner, const ModifierData *md) { - LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)md; + LaplacianDeformModifierData lmd = *(const LaplacianDeformModifierData *)md; + + if (ID_IS_OVERRIDE_LIBRARY(id_owner)) { + BLI_assert(!ID_IS_LINKED(id_owner)); + const bool is_local = (md->flag & eModifierFlag_OverrideLibrary_Local) != 0; + if (!is_local) { + /* Modifier coming from linked data cannot be bound from an override, so we can remove all + * binding data, can save a significant amount of memory. */ + lmd.verts_num = 0; + lmd.vertexco = NULL; + } + } - BLO_write_float3_array(writer, lmd->verts_num, lmd->vertexco); + BLO_write_struct_at_address(writer, LaplacianDeformModifierData, md, &lmd); + + if (lmd.vertexco != NULL) { + BLO_write_float3_array(writer, lmd.verts_num, lmd.vertexco); + } } static void blendRead(BlendDataReader *reader, ModifierData *md) diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 900dee98268..0813901fc49 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -91,7 +91,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } /* A vertex will be in the mask if a selected bone influences it more than a certain threshold. */ -static void compute_vertex_mask__armature_mode(MDeformVert *dvert, +static void compute_vertex_mask__armature_mode(const MDeformVert *dvert, Mesh *mesh, Object *armature_ob, float threshold, @@ -125,7 +125,7 @@ static void compute_vertex_mask__armature_mode(MDeformVert *dvert, } /* A vertex will be in the mask if the vertex group influences it more than a certain threshold. */ -static void compute_vertex_mask__vertex_group_mode(MDeformVert *dvert, +static void compute_vertex_mask__vertex_group_mode(const MDeformVert *dvert, int defgrp_index, float threshold, MutableSpan<bool> r_vertex_mask) @@ -347,7 +347,7 @@ static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, } static float get_interp_factor_from_vgroup( - MDeformVert *dvert, int defgrp_index, float threshold, uint v1, uint v2) + const MDeformVert *dvert, int defgrp_index, float threshold, uint v1, uint v2) { /* NOTE: this calculation is done twice for every vertex, * instead of storing it the first time and then reusing it. */ @@ -360,7 +360,7 @@ static void add_interp_verts_copy_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<bool> vertex_mask, Span<int> vertex_map, - MDeformVert *dvert, + const MDeformVert *dvert, int defgrp_index, float threshold, uint edges_masked_num, @@ -478,7 +478,7 @@ static void add_interpolated_polys_to_new_mesh(const Mesh &src_mesh, Span<bool> vertex_mask, Span<int> vertex_map, Span<int> edge_map, - MDeformVert *dvert, + const MDeformVert *dvert, int defgrp_index, float threshold, Span<int> masked_poly_indices, @@ -619,7 +619,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) (mmd->flag & MOD_MASK_SMOOTH); /* Return empty or input mesh when there are no vertex groups. */ - MDeformVert *dvert = (MDeformVert *)CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); + const MDeformVert *dvert = (const MDeformVert *)CustomData_get_layer(&mesh->vdata, + CD_MDEFORMVERT); if (dvert == nullptr) { return invert_mask ? mesh : BKE_mesh_new_nomain_from_template(mesh, 0, 0, 0, 0, 0); } diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c index c2e9e5ebe7d..0cff85d30ec 100644 --- a/source/blender/modifiers/intern/MOD_meshdeform.c +++ b/source/blender/modifiers/intern/MOD_meshdeform.c @@ -581,26 +581,49 @@ static void panelRegister(ARegionType *region_type) modifier_panel_register(region_type, eModifierType_MeshDeform, panel_draw); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *id_owner, const ModifierData *md) { - MeshDeformModifierData *mmd = (MeshDeformModifierData *)md; - int size = mmd->dyngridsize; + MeshDeformModifierData mmd = *(const MeshDeformModifierData *)md; + + if (ID_IS_OVERRIDE_LIBRARY(id_owner)) { + BLI_assert(!ID_IS_LINKED(id_owner)); + const bool is_local = (md->flag & eModifierFlag_OverrideLibrary_Local) != 0; + if (!is_local) { + /* Modifier coming from linked data cannot be bound from an override, so we can remove all + * binding data, can save a significant amount of memory. */ + mmd.influences_num = 0; + mmd.bindinfluences = NULL; + mmd.verts_num = 0; + mmd.bindoffsets = NULL; + mmd.cage_verts_num = 0; + mmd.bindcagecos = NULL; + mmd.dyngridsize = 0; + mmd.dyngrid = NULL; + mmd.influences_num = 0; + mmd.dyninfluences = NULL; + mmd.dynverts = NULL; + } + } - BLO_write_struct_array(writer, MDefInfluence, mmd->influences_num, mmd->bindinfluences); + const int size = mmd.dyngridsize; + + BLO_write_struct_at_address(writer, MeshDeformModifierData, md, &mmd); + + BLO_write_struct_array(writer, MDefInfluence, mmd.influences_num, mmd.bindinfluences); /* NOTE: `bindoffset` is abusing `verts_num + 1` as its size, this becomes an incorrect value in * case `verts_num == 0`, since `bindoffset` is then NULL, not a size 1 allocated array. */ - if (mmd->verts_num > 0) { - BLO_write_int32_array(writer, mmd->verts_num + 1, mmd->bindoffsets); + if (mmd.verts_num > 0) { + BLO_write_int32_array(writer, mmd.verts_num + 1, mmd.bindoffsets); } else { - BLI_assert(mmd->bindoffsets == NULL); + BLI_assert(mmd.bindoffsets == NULL); } - BLO_write_float3_array(writer, mmd->cage_verts_num, mmd->bindcagecos); - BLO_write_struct_array(writer, MDefCell, size * size * size, mmd->dyngrid); - BLO_write_struct_array(writer, MDefInfluence, mmd->influences_num, mmd->dyninfluences); - BLO_write_int32_array(writer, mmd->verts_num, mmd->dynverts); + BLO_write_float3_array(writer, mmd.cage_verts_num, mmd.bindcagecos); + BLO_write_struct_array(writer, MDefCell, size * size * size, mmd.dyngrid); + BLO_write_struct_array(writer, MDefInfluence, mmd.influences_num, mmd.dyninfluences); + BLO_write_int32_array(writer, mmd.verts_num, mmd.dynverts); } static void blendRead(BlendDataReader *reader, ModifierData *md) diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.cc b/source/blender/modifiers/intern/MOD_meshsequencecache.cc index 998fb0a94a3..273050eafd8 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.cc +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.cc @@ -5,8 +5,9 @@ */ #include <cstring> +#include <limits> -#include "BLI_math_vector.h" +#include "BLI_math_vector.hh" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -15,7 +16,6 @@ #include "DNA_cachefile_types.h" #include "DNA_defaults.h" #include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -42,6 +42,8 @@ #include "DEG_depsgraph_build.h" #include "DEG_depsgraph_query.h" +#include "GEO_mesh_primitive_cuboid.hh" + #include "MOD_modifiertypes.h" #include "MOD_ui_common.h" @@ -104,40 +106,17 @@ static bool isDisabled(const struct Scene *UNUSED(scene), return (mcmd->cache_file == nullptr) || (mcmd->object_path[0] == '\0'); } -static Mesh *generate_bounding_box_mesh(Object *object, Mesh *org_mesh) +static Mesh *generate_bounding_box_mesh(const Mesh *org_mesh) { - const BoundBox *bb = BKE_object_boundbox_get(object); - Mesh *result = BKE_mesh_new_nomain_from_template(org_mesh, 8, 0, 0, 24, 6); - - MVert *mvert = result->mvert; - for (int i = 0; i < 8; ++i) { - copy_v3_v3(mvert[i].co, bb->vec[i]); - } - - /* See DNA_object_types.h for the diagram showing the order of the vertices for a BoundBox. */ - static unsigned int loops_v[6][4] = { - {0, 4, 5, 1}, - {4, 7, 6, 5}, - {7, 3, 2, 6}, - {3, 0, 1, 2}, - {1, 5, 6, 2}, - {3, 7, 4, 0}, - }; - - MLoop *mloop = result->mloop; - for (int i = 0; i < 6; ++i) { - for (int j = 0; j < 4; ++j, ++mloop) { - mloop->v = loops_v[i][j]; - } - } - - MPoly *mpoly = result->mpoly; - for (int i = 0; i < 6; ++i) { - mpoly[i].loopstart = i * 4; - mpoly[i].totloop = 4; + using namespace blender; + float3 min(std::numeric_limits<float>::max()); + float3 max(-std::numeric_limits<float>::max()); + if (!BKE_mesh_minmax(org_mesh, min, max)) { + return nullptr; } - BKE_mesh_calc_edges(result, false, false); + Mesh *result = geometry::create_cuboid_mesh(max - min, 2, 2, 2); + BKE_mesh_translate(result, math::midpoint(min, max), false); return result; } @@ -170,7 +149,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * /* Do not process data if using a render procedural, return a box instead for displaying in the * viewport. */ if (BKE_cache_file_uses_render_procedural(cache_file, scene)) { - return generate_bounding_box_mesh(ctx->object, org_mesh); + return generate_bounding_box_mesh(org_mesh); } /* If this invocation is for the ORCO mesh, and the mesh hasn't changed topology, we diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 21041e8e1b2..0d8cef3e083 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -985,17 +985,16 @@ static Vector<OutputAttributeToStore> compute_attributes_to_store( if (!component.attribute_domain_supported(domain)) { continue; } - const int domain_size = component.attribute_domain_size(domain); + const int domain_num = component.attribute_domain_num(domain); blender::bke::GeometryComponentFieldContext field_context{component, domain}; - blender::fn::FieldEvaluator field_evaluator{field_context, domain_size}; + blender::fn::FieldEvaluator field_evaluator{field_context, domain_num}; for (const OutputAttributeInfo &output_info : outputs_info) { const CPPType &type = output_info.field.cpp_type(); OutputAttributeToStore store{ component_type, domain, output_info.name, - GMutableSpan{ - type, MEM_malloc_arrayN(domain_size, type.size(), __func__), domain_size}}; + GMutableSpan{type, MEM_malloc_arrayN(domain_num, type.size(), __func__), domain_num}}; field_evaluator.add_with_destination(output_info.field, store.data); attributes_to_store.append(store); } @@ -1730,9 +1729,12 @@ static void panelRegister(ARegionType *region_type) panel_type); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) { const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md); + + BLO_write_struct(writer, NodesModifierData, nmd); + if (nmd->settings.properties != nullptr) { /* Note that the property settings are based on the socket type info * and don't necessarily need to be written, but we can't just free them. */ @@ -1799,7 +1801,7 @@ ModifierTypeInfo modifierType_Nodes = { eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping), - /* icon */ ICON_NODETREE, + /* icon */ ICON_GEOMETRY_NODES, /* copyData */ copyData, diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index fe05f48a868..c215ac601a1 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -136,7 +136,7 @@ static void mix_normals(const float mix_factor, if (dvert) { facs = MEM_malloc_arrayN((size_t)loops_num, sizeof(*facs), __func__); BKE_defvert_extract_vgroup_to_loopweights( - dvert, defgrp_index, verts_num, mloop, loops_num, facs, use_invert_vgroup); + dvert, defgrp_index, verts_num, mloop, loops_num, use_invert_vgroup, facs); } for (i = loops_num, no_new = nos_new, no_old = nos_old, wfac = facs; i--; @@ -532,14 +532,13 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd, MDeformVert *dvert; float(*loopnors)[3] = NULL; - short(*clnors)[2] = NULL; CustomData *ldata = &result->ldata; const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(result); const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(result); - clnors = CustomData_get_layer(ldata, CD_CUSTOMLOOPNORMAL); + short(*clnors)[2] = CustomData_get_layer(ldata, CD_CUSTOMLOOPNORMAL); if (use_current_clnors) { clnors = CustomData_duplicate_referenced_layer(ldata, CD_CUSTOMLOOPNORMAL, loops_num); loopnors = MEM_malloc_arrayN((size_t)loops_num, sizeof(*loopnors), __func__); diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.cc index 032227307e7..c6a606360e3 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.c +++ b/source/blender/modifiers/intern/MOD_particlesystem.cc @@ -5,8 +5,8 @@ * \ingroup modifiers */ -#include <stddef.h> -#include <string.h> +#include <cstddef> +#include <cstring> #include "BLI_utildefines.h" @@ -51,11 +51,11 @@ static void freeData(ModifierData *md) ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; if (psmd->mesh_final) { - BKE_id_free(NULL, psmd->mesh_final); - psmd->mesh_final = NULL; + BKE_id_free(nullptr, psmd->mesh_final); + psmd->mesh_final = nullptr; if (psmd->mesh_original) { - BKE_id_free(NULL, psmd->mesh_original); - psmd->mesh_original = NULL; + BKE_id_free(nullptr, psmd->mesh_original); + psmd->mesh_original = nullptr; } } psmd->totdmvert = psmd->totdmedge = psmd->totdmface = 0; @@ -81,8 +81,8 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla * code has to be called then to ensure proper remapping of that pointer. See e.g. * `BKE_object_copy_particlesystems` or `BKE_object_copy_modifier`. */ - tpsmd->mesh_final = NULL; - tpsmd->mesh_original = NULL; + tpsmd->mesh_final = nullptr; + tpsmd->mesh_original = nullptr; tpsmd->totdmvert = tpsmd->totdmedge = tpsmd->totdmface = 0; } @@ -104,7 +104,7 @@ static void deformVerts(ModifierData *md, { Mesh *mesh_src = mesh; ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; - ParticleSystem *psys = NULL; + ParticleSystem *psys = nullptr; if (ctx->object->particlesystem.first) { psys = psmd->psys; @@ -117,28 +117,28 @@ static void deformVerts(ModifierData *md, return; } - if (mesh_src == NULL) { + if (mesh_src == nullptr) { mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, NULL, vertexCos, verts_num, false, true); - if (mesh_src == NULL) { + ctx->object, nullptr, nullptr, vertexCos, verts_num, false, true); + if (mesh_src == nullptr) { return; } } /* clear old dm */ - bool had_mesh_final = (psmd->mesh_final != NULL); + bool had_mesh_final = (psmd->mesh_final != nullptr); if (psmd->mesh_final) { - BKE_id_free(NULL, psmd->mesh_final); - psmd->mesh_final = NULL; + BKE_id_free(nullptr, psmd->mesh_final); + psmd->mesh_final = nullptr; if (psmd->mesh_original) { - BKE_id_free(NULL, psmd->mesh_original); - psmd->mesh_original = NULL; + BKE_id_free(nullptr, psmd->mesh_original); + psmd->mesh_original = nullptr; } } else if (psmd->flag & eParticleSystemFlag_file_loaded) { /* in file read mesh just wasn't saved in file so no need to reset everything */ psmd->flag &= ~eParticleSystemFlag_file_loaded; - if (psys->particles == NULL) { + if (psys->particles == nullptr) { psys->recalc |= ID_RECALC_PSYS_RESET; } /* TODO(sergey): This is not how particles were working prior to copy on @@ -165,18 +165,18 @@ static void deformVerts(ModifierData *md, /* Get the original mesh from the object, this is what the particles * are attached to so in case of non-deform modifiers we need to remap * them to the final mesh (typically subdivision surfaces). */ - Mesh *mesh_original = NULL; + Mesh *mesh_original = nullptr; if (ctx->object->type == OB_MESH) { BMEditMesh *em = BKE_editmesh_from_object(ctx->object); if (em) { /* In edit mode get directly from the edit mesh. */ - psmd->mesh_original = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, NULL, mesh); + psmd->mesh_original = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, nullptr, mesh); } else { /* Otherwise get regular mesh. */ - mesh_original = ctx->object->data; + mesh_original = static_cast<Mesh *>(ctx->object->data); } } else { @@ -193,8 +193,8 @@ static void deformVerts(ModifierData *md, BKE_mesh_tessface_ensure(psmd->mesh_original); } - if (!ELEM(mesh_src, NULL, mesh, psmd->mesh_final)) { - BKE_id_free(NULL, mesh_src); + if (!ELEM(mesh_src, nullptr, mesh, psmd->mesh_final)) { + BKE_id_free(nullptr, mesh_src); } /* Report change in mesh structure. @@ -221,7 +221,7 @@ static void deformVerts(ModifierData *md, if (DEG_is_active(ctx->depsgraph)) { Object *object_orig = DEG_get_original_object(ctx->object); ModifierData *md_orig = BKE_modifiers_findby_name(object_orig, psmd->modifier.name); - BLI_assert(md_orig != NULL); + BLI_assert(md_orig != nullptr); ParticleSystemModifierData *psmd_orig = (ParticleSystemModifierData *)md_orig; psmd_orig->flag = psmd->flag; } @@ -237,16 +237,16 @@ static void deformVertsEM(ModifierData *md, float (*vertexCos)[3], int verts_num) { - const bool do_temp_mesh = (mesh == NULL); + const bool do_temp_mesh = (mesh == nullptr); if (do_temp_mesh) { mesh = BKE_id_new_nomain(ID_ME, ((ID *)ob->data)->name); - BM_mesh_bm_to_me(NULL, editData->bm, mesh, &((BMeshToMeshParams){0})); + BM_mesh_bm_to_me(nullptr, editData->bm, mesh, &((BMeshToMeshParams){0})); } deformVerts(md, ob, mesh, vertexCos, verts_num); if (derivedData) { - BKE_id_free(NULL, mesh); + BKE_id_free(nullptr, mesh); } } #endif @@ -258,7 +258,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA ob_ptr; PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); - Object *ob = ob_ptr.data; + Object *ob = static_cast<Object *>(ob_ptr.data); ModifierData *md = (ModifierData *)ptr->data; ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; @@ -291,8 +291,8 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) { ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; - psmd->mesh_final = NULL; - psmd->mesh_original = NULL; + psmd->mesh_final = nullptr; + psmd->mesh_original = nullptr; /* This is written as part of ob->particlesystem. */ BLO_read_data_address(reader, &psmd->psys); psmd->flag &= ~eParticleSystemFlag_psys_updated; @@ -315,23 +315,23 @@ ModifierTypeInfo modifierType_ParticleSystem = { /* copyData */ copyData, /* deformVerts */ deformVerts, - /* deformMatrices */ NULL, - /* deformVertsEM */ NULL, - /* deformMatricesEM */ NULL, - /* modifyMesh */ NULL, - /* modifyGeometrySet */ NULL, + /* deformMatrices */ nullptr, + /* deformVertsEM */ nullptr, + /* deformMatricesEM */ nullptr, + /* modifyMesh */ nullptr, + /* modifyGeometrySet */ nullptr, /* initData */ initData, /* requiredDataMask */ requiredDataMask, /* freeData */ freeData, - /* isDisabled */ NULL, - /* updateDepsgraph */ NULL, - /* dependsOnTime */ NULL, - /* dependsOnNormals */ NULL, - /* foreachIDLink */ NULL, - /* foreachTexLink */ NULL, - /* freeRuntimeData */ NULL, + /* isDisabled */ nullptr, + /* updateDepsgraph */ nullptr, + /* dependsOnTime */ nullptr, + /* dependsOnNormals */ nullptr, + /* foreachIDLink */ nullptr, + /* foreachTexLink */ nullptr, + /* freeRuntimeData */ nullptr, /* panelRegister */ panelRegister, - /* blendWrite */ NULL, + /* blendWrite */ nullptr, /* blendRead */ blendRead, }; diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 08925e8aeb1..0e22f59c2fb 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -182,7 +182,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * ScrewModifierData *ltmd = (ScrewModifierData *)md; const bool use_render_params = (ctx->flag & MOD_APPLY_RENDER) != 0; - int *origindex; int mpoly_index = 0; uint step; uint i, j; @@ -375,6 +374,12 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * uv_u_scale = (uv_u_scale / (float)ltmd->iter) * (angle / ((float)M_PI * 2.0f)); } + /* The `screw_ofs` cannot change from now on. */ + const bool do_remove_doubles = (ltmd->flag & MOD_SCREW_MERGE) && (screw_ofs == 0.0f); + /* Only calculate normals if `do_remove_doubles` since removing doubles frees the normals. */ + const bool do_normal_create = (ltmd->flag & MOD_SCREW_NORMAL_CALC) && + (do_remove_doubles == false); + result = BKE_mesh_new_nomain_from_template( mesh, (int)maxVerts, (int)maxEdges, 0, (int)maxPolys * 4, (int)maxPolys); @@ -383,7 +388,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * medge_orig = mesh->medge; mvert_new = result->mvert; - float(*vert_normals_new)[3] = BKE_mesh_vertex_normals_for_write(result); mpoly_new = result->mpoly; mloop_new = result->mloop; medge_new = result->medge; @@ -392,7 +396,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * CustomData_add_layer(&result->pdata, CD_ORIGINDEX, CD_CALLOC, NULL, (int)maxPolys); } - origindex = CustomData_get_layer(&result->pdata, CD_ORIGINDEX); + int *origindex = CustomData_get_layer(&result->pdata, CD_ORIGINDEX); CustomData_copy_data(&mesh->vdata, &result->vdata, 0, 0, (int)totvert); @@ -472,7 +476,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } } + float(*vert_normals_new)[3] = do_normal_create ? BKE_mesh_vertex_normals_for_write(result) : + NULL; + if (ltmd->flag & MOD_SCREW_NORMAL_CALC) { + /* * Normal Calculation (for face flipping) * Sort edge verts for correct face flipping @@ -766,68 +774,69 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * * * calculate vertex normals that can be propagated on lathing * use edge connectivity work this out */ - if (SV_IS_VALID(vc->v[0])) { - if (SV_IS_VALID(vc->v[1])) { - /* 2 edges connected. */ - /* make 2 connecting vert locations relative to the middle vert */ - sub_v3_v3v3(tmp_vec1, mvert_new[vc->v[0]].co, mvert_new[i].co); - sub_v3_v3v3(tmp_vec2, mvert_new[vc->v[1]].co, mvert_new[i].co); - /* normalize so both edges have the same influence, no matter their length */ - normalize_v3(tmp_vec1); - normalize_v3(tmp_vec2); - - /* vc_no_tmp1 - this line is the average direction of both connecting edges - * - * Use the edge order to make the subtraction, flip the normal the right way - * edge should be there but check just in case... */ - if (vc->e[0]->v1 == i) { - sub_v3_v3(tmp_vec1, tmp_vec2); - } - else { - sub_v3_v3v3(tmp_vec1, tmp_vec2, tmp_vec1); - } - } - else { - /* only 1 edge connected - same as above except - * don't need to average edge direction */ - if (vc->e[0]->v2 == i) { - sub_v3_v3v3(tmp_vec1, mvert_new[i].co, mvert_new[vc->v[0]].co); + if (do_normal_create) { + if (SV_IS_VALID(vc->v[0])) { + if (SV_IS_VALID(vc->v[1])) { + /* 2 edges connected. */ + /* make 2 connecting vert locations relative to the middle vert */ + sub_v3_v3v3(tmp_vec1, mvert_new[vc->v[0]].co, mvert_new[i].co); + sub_v3_v3v3(tmp_vec2, mvert_new[vc->v[1]].co, mvert_new[i].co); + /* normalize so both edges have the same influence, no matter their length */ + normalize_v3(tmp_vec1); + normalize_v3(tmp_vec2); + + /* vc_no_tmp1 - this line is the average direction of both connecting edges + * + * Use the edge order to make the subtraction, flip the normal the right way + * edge should be there but check just in case... */ + if (vc->e[0]->v1 == i) { + sub_v3_v3(tmp_vec1, tmp_vec2); + } + else { + sub_v3_v3v3(tmp_vec1, tmp_vec2, tmp_vec1); + } } else { - sub_v3_v3v3(tmp_vec1, mvert_new[vc->v[0]].co, mvert_new[i].co); + /* only 1 edge connected - same as above except + * don't need to average edge direction */ + if (vc->e[0]->v2 == i) { + sub_v3_v3v3(tmp_vec1, mvert_new[i].co, mvert_new[vc->v[0]].co); + } + else { + sub_v3_v3v3(tmp_vec1, mvert_new[vc->v[0]].co, mvert_new[i].co); + } } - } - /* tmp_vec2 - is a line 90d from the pivot to the vec - * This is used so the resulting normal points directly away from the middle */ - cross_v3_v3v3(tmp_vec2, axis_vec, vc->co); + /* tmp_vec2 - is a line 90d from the pivot to the vec + * This is used so the resulting normal points directly away from the middle */ + cross_v3_v3v3(tmp_vec2, axis_vec, vc->co); - if (UNLIKELY(is_zero_v3(tmp_vec2))) { - /* we're _on_ the axis, so copy it based on our winding */ - if (vc->e[0]->v2 == i) { - negate_v3_v3(vc->no, axis_vec); + if (UNLIKELY(is_zero_v3(tmp_vec2))) { + /* we're _on_ the axis, so copy it based on our winding */ + if (vc->e[0]->v2 == i) { + negate_v3_v3(vc->no, axis_vec); + } + else { + copy_v3_v3(vc->no, axis_vec); + } } else { - copy_v3_v3(vc->no, axis_vec); + /* edge average vector and right angle to the pivot make the normal */ + cross_v3_v3v3(vc->no, tmp_vec1, tmp_vec2); } } else { - /* edge average vector and right angle to the pivot make the normal */ - cross_v3_v3v3(vc->no, tmp_vec1, tmp_vec2); + copy_v3_v3(vc->no, vc->co); } - } - else { - copy_v3_v3(vc->no, vc->co); - } - - /* we won't be looping on this data again so copy normals here */ - if ((angle < 0.0f) != do_flip) { - negate_v3(vc->no); - } - normalize_v3(vc->no); - copy_v3_v3(vert_normals_new[i], vc->no); + /* we won't be looping on this data again so copy normals here */ + if ((angle < 0.0f) != do_flip) { + negate_v3(vc->no); + } + normalize_v3(vc->no); + copy_v3_v3(vert_normals_new[i], vc->no); + } /* Done with normals */ } } @@ -846,7 +855,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * for (step = 1; step < step_tot; step++) { const uint varray_stride = totvert * step; float step_angle; - float nor_tx[3]; float mat[4][4]; /* Rotation Matrix */ step_angle = (angle / (float)(step_tot - (!close))) * (float)step; @@ -872,10 +880,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * for (j = 0; j < totvert; j++, mv_new_base++, mv_new++) { /* set normal */ if (vert_connect) { - mul_v3_m3v3(nor_tx, mat3, vert_connect[j].no); - - /* set the normal now its transformed */ - copy_v3_v3(vert_normals_new[mv_new - mvert_new], nor_tx); + if (do_normal_create) { + /* Set the normal now its transformed. */ + mul_v3_m3v3(vert_normals_new[mv_new - mvert_new], mat3, vert_connect[j].no); + } } /* set location */ @@ -1118,11 +1126,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * MEM_freeN(vert_loop_map); } - if ((ltmd->flag & MOD_SCREW_NORMAL_CALC)) { + if (do_normal_create) { BKE_mesh_vertex_normals_clear_dirty(result); } - if ((ltmd->flag & MOD_SCREW_MERGE) && (screw_ofs == 0.0f)) { + if (do_remove_doubles) { result = mesh_remove_doubles_on_axis(result, mvert_new, totvert, diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index 7f96dcb82fb..5f238209015 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -881,34 +881,29 @@ static int calc_edge_subdivisions(const MVert *mvert, /* Take a Mesh and subdivide its edges to keep skin nodes * reasonably close. */ -static Mesh *subdivide_base(Mesh *orig) +static Mesh *subdivide_base(const Mesh *orig) { - Mesh *result; - MVertSkin *orignode, *outnode; - MVert *origvert, *outvert; - MEdge *origedge, *outedge, *e; - MDeformVert *origdvert, *outdvert; - int orig_vert_num, orig_edge_num; - int subd_num, *degree, *edge_subd; + const MEdge *e; + int subd_num; int i, j, k, u, v; float radrat; - orignode = CustomData_get_layer(&orig->vdata, CD_MVERT_SKIN); - origvert = orig->mvert; - origedge = orig->medge; - origdvert = orig->dvert; - orig_vert_num = orig->totvert; - orig_edge_num = orig->totedge; + const MVertSkin *orignode = CustomData_get_layer(&orig->vdata, CD_MVERT_SKIN); + const MVert *origvert = orig->mvert; + const MEdge *origedge = orig->medge; + const MDeformVert *origdvert = orig->dvert; + int orig_vert_num = orig->totvert; + int orig_edge_num = orig->totedge; /* Get degree of all vertices */ - degree = MEM_calloc_arrayN(orig_vert_num, sizeof(int), "degree"); + int *degree = MEM_calloc_arrayN(orig_vert_num, sizeof(int), "degree"); for (i = 0; i < orig_edge_num; i++) { degree[origedge[i].v1]++; degree[origedge[i].v2]++; } /* Per edge, store how many subdivisions are needed */ - edge_subd = MEM_calloc_arrayN((uint)orig_edge_num, sizeof(int), "edge_subd"); + int *edge_subd = MEM_calloc_arrayN((uint)orig_edge_num, sizeof(int), "edge_subd"); for (i = 0, subd_num = 0; i < orig_edge_num; i++) { edge_subd[i] += calc_edge_subdivisions(origvert, orignode, &origedge[i], degree); BLI_assert(edge_subd[i] >= 0); @@ -918,13 +913,13 @@ static Mesh *subdivide_base(Mesh *orig) MEM_freeN(degree); /* Allocate output mesh */ - result = BKE_mesh_new_nomain_from_template( + Mesh *result = BKE_mesh_new_nomain_from_template( orig, orig_vert_num + subd_num, orig_edge_num + subd_num, 0, 0, 0); - outvert = result->mvert; - outedge = result->medge; - outnode = CustomData_get_layer(&result->vdata, CD_MVERT_SKIN); - outdvert = result->dvert; + MVert *outvert = result->mvert; + MEdge *outedge = result->medge; + MVertSkin *outnode = CustomData_get_layer(&result->vdata, CD_MVERT_SKIN); + MDeformVert *outdvert = result->dvert; /* Copy original vertex data */ CustomData_copy_data(&orig->vdata, &result->vdata, 0, 0, orig_vert_num); diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c index 48154a3670d..1f0aee7d689 100644 --- a/source/blender/modifiers/intern/MOD_solidify.c +++ b/source/blender/modifiers/intern/MOD_solidify.c @@ -75,7 +75,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * case MOD_SOLIDIFY_MODE_NONMANIFOLD: return MOD_solidify_nonmanifold_modifyMesh(md, ctx, mesh); default: - BLI_assert(0); + BLI_assert_unreachable(); } return mesh; } diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c index a80918b8d2b..9b0012e3890 100644 --- a/source/blender/modifiers/intern/MOD_surfacedeform.c +++ b/source/blender/modifiers/intern/MOD_surfacedeform.c @@ -1166,8 +1166,8 @@ static bool surfacedeformBind(Object *ob, SurfaceDeformModifierData *smd_eval, float (*vertexCos)[3], uint verts_num, - uint tpolys_num, - uint tverts_num, + uint target_polys_num, + uint target_verts_num, Mesh *target, Mesh *mesh) { @@ -1182,7 +1182,7 @@ static bool surfacedeformBind(Object *ob, SDefAdjacency *adj_array; SDefEdgePolys *edge_polys; - vert_edges = MEM_calloc_arrayN(tverts_num, sizeof(*vert_edges), "SDefVertEdgeMap"); + vert_edges = MEM_calloc_arrayN(target_verts_num, sizeof(*vert_edges), "SDefVertEdgeMap"); if (vert_edges == NULL) { BKE_modifier_set_error(ob, (ModifierData *)smd_eval, "Out of memory"); return false; @@ -1220,7 +1220,7 @@ static bool surfacedeformBind(Object *ob, } adj_result = buildAdjacencyMap( - mpoly, medge, mloop, tpolys_num, tedges_num, vert_edges, adj_array, edge_polys); + mpoly, medge, mloop, target_polys_num, tedges_num, vert_edges, adj_array, edge_polys); if (adj_result == MOD_SDEF_BIND_RESULT_NONMANY_ERR) { BKE_modifier_set_error( @@ -1233,7 +1233,8 @@ static bool surfacedeformBind(Object *ob, } smd_orig->mesh_verts_num = verts_num; - smd_orig->polys_num = tpolys_num; + smd_orig->target_verts_num = target_verts_num; + smd_orig->target_polys_num = target_polys_num; int defgrp_index; MDeformVert *dvert; @@ -1249,7 +1250,8 @@ static bool surfacedeformBind(Object *ob, .medge = medge, .mloop = mloop, .looptri = BKE_mesh_runtime_looptri_ensure(target), - .targetCos = MEM_malloc_arrayN(tverts_num, sizeof(float[3]), "SDefTargetBindVertArray"), + .targetCos = MEM_malloc_arrayN( + target_verts_num, sizeof(float[3]), "SDefTargetBindVertArray"), .bind_verts = smd_orig->verts, .vertexCos = vertexCos, .falloff = smd_orig->falloff, @@ -1268,7 +1270,7 @@ static bool surfacedeformBind(Object *ob, invert_m4_m4(data.imat, smd_orig->mat); - for (int i = 0; i < tverts_num; i++) { + for (int i = 0; i < target_verts_num; i++) { mul_v3_m4v3(data.targetCos[i], smd_orig->mat, mvert[i].co); } @@ -1431,7 +1433,7 @@ static void surfacedeformModifier_do(ModifierData *md, { SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md; Mesh *target; - uint tverts_num, tpolys_num; + uint target_verts_num, target_polys_num; /* Exit function if bind flag is not set (free bind data if any). */ if (!(smd->flags & MOD_SDEF_BIND)) { @@ -1453,8 +1455,8 @@ static void surfacedeformModifier_do(ModifierData *md, return; } - tverts_num = BKE_mesh_wrapper_vert_len(target); - tpolys_num = BKE_mesh_wrapper_poly_len(target); + target_verts_num = BKE_mesh_wrapper_vert_len(target); + target_polys_num = BKE_mesh_wrapper_poly_len(target); /* If not bound, execute bind. */ if (smd->verts == NULL) { @@ -1473,25 +1475,62 @@ static void surfacedeformModifier_do(ModifierData *md, /* Avoid converting edit-mesh data, binding is an exception. */ BKE_mesh_wrapper_ensure_mdata(target); - if (!surfacedeformBind( - ob, smd_orig, smd, vertexCos, verts_num, tpolys_num, tverts_num, target, mesh)) { + if (!surfacedeformBind(ob, + smd_orig, + smd, + vertexCos, + verts_num, + target_polys_num, + target_verts_num, + target, + mesh)) { smd->flags &= ~MOD_SDEF_BIND; } /* Early abort, this is binding 'call', no need to perform whole evaluation. */ return; } - /* Poly count checks */ + /* Geometry count on the deforming mesh. */ if (smd->mesh_verts_num != verts_num) { BKE_modifier_set_error( ob, md, "Vertices changed from %u to %u", smd->mesh_verts_num, verts_num); return; } - if (smd->polys_num != tpolys_num) { + + /* Geometry count on the target mesh. */ + if (smd->target_polys_num != target_polys_num && smd->target_verts_num == 0) { + /* Change in the number of polygons does not really imply change in the vertex count, but + * this is how the modifier worked before the vertex count was known. Follow the legacy + * logic without requirement to re-bind the mesh. */ BKE_modifier_set_error( - ob, md, "Target polygons changed from %u to %u", smd->polys_num, tpolys_num); + ob, md, "Target polygons changed from %u to %u", smd->target_polys_num, target_polys_num); return; } + if (smd->target_verts_num != 0 && smd->target_verts_num != target_verts_num) { + if (smd->target_verts_num > target_verts_num) { + /* Number of vertices on the target did reduce. There is no usable recovery from this. */ + BKE_modifier_set_error(ob, + md, + "Target vertices changed from %u to %u", + smd->target_verts_num, + target_verts_num); + return; + } + + /* Assume the increase in the vertex count means that the "new" vertices in the target mesh are + * added after the original ones. This covers typical case when target was at the subdivision + * level 0 and then subdivision was increased (i.e. for the render purposes). */ + + BKE_modifier_set_error(ob, + md, + "Target vertices changed from %u to %u, continuing anyway", + smd->target_verts_num, + target_verts_num); + + /* In theory we only need the `smd->verts_num` vertices in the `targetCos` for evaluation, but + * it is not currently possible to request a subset of coordinates: the API expects that the + * caller needs coordinates of all vertices and asserts for it. */ + } /* Early out if modifier would not affect input at all - still *after* the sanity checks * (and potential binding) above. */ @@ -1507,7 +1546,7 @@ static void surfacedeformModifier_do(ModifierData *md, /* Actual vertex location update starts here */ SDefDeformData data = { .bind_verts = smd->verts, - .targetCos = MEM_malloc_arrayN(tverts_num, sizeof(float[3]), "SDefTargetVertArray"), + .targetCos = MEM_malloc_arrayN(target_verts_num, sizeof(float[3]), "SDefTargetVertArray"), .vertexCos = vertexCos, .dvert = dvert, .defgrp_index = defgrp_index, @@ -1516,7 +1555,8 @@ static void surfacedeformModifier_do(ModifierData *md, }; if (data.targetCos != NULL) { - BKE_mesh_wrapper_vert_coords_copy_with_mat4(target, data.targetCos, tverts_num, smd->mat); + BKE_mesh_wrapper_vert_coords_copy_with_mat4( + target, data.targetCos, target_verts_num, smd->mat); TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); @@ -1629,27 +1669,41 @@ static void panelRegister(ARegionType *region_type) modifier_panel_register(region_type, eModifierType_SurfaceDeform, panel_draw); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *id_owner, const ModifierData *md) { - const SurfaceDeformModifierData *smd = (const SurfaceDeformModifierData *)md; + SurfaceDeformModifierData smd = *(const SurfaceDeformModifierData *)md; + + if (ID_IS_OVERRIDE_LIBRARY(id_owner)) { + BLI_assert(!ID_IS_LINKED(id_owner)); + const bool is_local = (md->flag & eModifierFlag_OverrideLibrary_Local) != 0; + if (!is_local) { + /* Modifier coming from linked data cannot be bound from an override, so we can remove all + * binding data, can save a significant amount of memory. */ + smd.bind_verts_num = 0; + smd.verts = NULL; + } + } - BLO_write_struct_array(writer, SDefVert, smd->bind_verts_num, smd->verts); + BLO_write_struct_at_address(writer, SurfaceDeformModifierData, md, &smd); - if (smd->verts) { - for (int i = 0; i < smd->bind_verts_num; i++) { - BLO_write_struct_array(writer, SDefBind, smd->verts[i].binds_num, smd->verts[i].binds); + if (smd.verts != NULL) { + SDefVert *bind_verts = smd.verts; + BLO_write_struct_array(writer, SDefVert, smd.bind_verts_num, bind_verts); - if (smd->verts[i].binds) { - for (int j = 0; j < smd->verts[i].binds_num; j++) { + for (int i = 0; i < smd.bind_verts_num; i++) { + BLO_write_struct_array(writer, SDefBind, bind_verts[i].binds_num, bind_verts[i].binds); + + if (bind_verts[i].binds) { + for (int j = 0; j < bind_verts[i].binds_num; j++) { BLO_write_uint32_array( - writer, smd->verts[i].binds[j].verts_num, smd->verts[i].binds[j].vert_inds); + writer, bind_verts[i].binds[j].verts_num, bind_verts[i].binds[j].vert_inds); - if (ELEM(smd->verts[i].binds[j].mode, MOD_SDEF_MODE_CENTROID, MOD_SDEF_MODE_LOOPTRI)) { - BLO_write_float3_array(writer, 1, smd->verts[i].binds[j].vert_weights); + if (ELEM(bind_verts[i].binds[j].mode, MOD_SDEF_MODE_CENTROID, MOD_SDEF_MODE_LOOPTRI)) { + BLO_write_float3_array(writer, 1, bind_verts[i].binds[j].vert_weights); } else { BLO_write_float_array( - writer, smd->verts[i].binds[j].verts_num, smd->verts[i].binds[j].vert_weights); + writer, bind_verts[i].binds[j].verts_num, bind_verts[i].binds[j].vert_weights); } } } diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index a58e8e23147..575182a846b 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -100,10 +100,9 @@ void MOD_get_texture_coords(MappingInfoModifierData *dmd, BLI_bitmap *done = BLI_BITMAP_NEW(verts_num, __func__); const int polys_num = mesh->totpoly; char uvname[MAX_CUSTOMDATA_LAYER_NAME]; - MLoopUV *mloop_uv; CustomData_validate_layer_name(&mesh->ldata, CD_MLOOPUV, dmd->uvlayer_name, uvname); - mloop_uv = CustomData_get_layer_named(&mesh->ldata, CD_MLOOPUV, uvname); + const MLoopUV *mloop_uv = CustomData_get_layer_named(&mesh->ldata, CD_MLOOPUV, uvname); /* verts are given the UV from the first face that uses them */ for (i = 0, mp = mpoly; i < polys_num; i++, mp++) { diff --git a/source/blender/modifiers/intern/MOD_util.h b/source/blender/modifiers/intern/MOD_util.h index aef254b1103..c37d6a3f2c1 100644 --- a/source/blender/modifiers/intern/MOD_util.h +++ b/source/blender/modifiers/intern/MOD_util.h @@ -11,6 +11,10 @@ #include "DEG_depsgraph_build.h" +#ifdef __cplusplus +extern "C" { +#endif + struct MDeformVert; struct Mesh; struct ModifierData; @@ -51,3 +55,7 @@ void MOD_depsgraph_update_object_bone_relation(struct DepsNodeHandle *node, struct Object *object, const char *bonename, const char *description); + +#ifdef __cplusplus +} +#endif
\ No newline at end of file diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index 79972d1911d..402d7b2c99e 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -489,10 +489,12 @@ static void panelRegister(ARegionType *region_type) region_type, "texture", "Texture", NULL, texture_panel_draw, panel_type); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) { const WarpModifierData *wmd = (const WarpModifierData *)md; + BLO_write_struct(writer, WarpModifierData, wmd); + if (wmd->curfalloff) { BKE_curvemapping_blend_write(writer, wmd->curfalloff); } diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c index c79dbdb0b1a..d436acb8ad5 100644 --- a/source/blender/modifiers/intern/MOD_weighted_normal.c +++ b/source/blender/modifiers/intern/MOD_weighted_normal.c @@ -82,7 +82,7 @@ typedef struct WeightedNormalData { MPoly *mpoly; const float (*polynors)[3]; - int *poly_strength; + const int *poly_strength; MDeformVert *dvert; const int defgrp_index; @@ -195,7 +195,7 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd, MPoly *mpoly = wn_data->mpoly; const float(*polynors)[3] = wn_data->polynors; - int *poly_strength = wn_data->poly_strength; + const int *poly_strength = wn_data->poly_strength; MDeformVert *dvert = wn_data->dvert; @@ -326,7 +326,7 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd, } break; default: - BLI_assert(0); + BLI_assert_unreachable(); } /* Validate computed weighted normals. */ @@ -603,15 +603,13 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } const float split_angle = mesh->smoothresh; - short(*clnors)[2]; - CustomData *ldata = &result->ldata; - clnors = CustomData_get_layer(ldata, CD_CUSTOMLOOPNORMAL); + short(*clnors)[2] = CustomData_get_layer(&result->ldata, CD_CUSTOMLOOPNORMAL); /* Keep info whether we had clnors, * it helps when generating clnor spaces and default normals. */ const bool has_clnors = clnors != NULL; if (!clnors) { - clnors = CustomData_add_layer(ldata, CD_CUSTOMLOOPNORMAL, CD_CALLOC, NULL, loops_num); + clnors = CustomData_add_layer(&result->ldata, CD_CUSTOMLOOPNORMAL, CD_CALLOC, NULL, loops_num); } MDeformVert *dvert; diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.c b/source/blender/modifiers/intern/MOD_weightvg_util.c index b251825cd95..65393370268 100644 --- a/source/blender/modifiers/intern/MOD_weightvg_util.c +++ b/source/blender/modifiers/intern/MOD_weightvg_util.c @@ -96,7 +96,7 @@ void weightvg_do_map( BLI_assert(do_invert); break; default: - BLI_assert(0); + BLI_assert_unreachable(); } new_w[i] = do_invert ? 1.0f - fac : fac; @@ -206,8 +206,6 @@ void weightvg_do_mask(const ModifierEvalContext *ctx, MEM_freeN(tex_co); } else if ((ref_didx = BKE_id_defgroup_name_index(&mesh->id, defgrp_name)) != -1) { - MDeformVert *dvert = NULL; - /* Check whether we want to set vgroup weights from a constant weight factor or a vertex * group. */ @@ -215,7 +213,7 @@ void weightvg_do_mask(const ModifierEvalContext *ctx, /* Proceed only if vgroup is valid, else use constant factor. */ /* Get actual dverts (ie vertex group data). */ - dvert = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); + const MDeformVert *dvert = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); /* Proceed only if vgroup is valid, else assume factor = O. */ if (dvert == NULL) { return; diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index 2c733542e51..e1b43157adb 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -375,10 +375,12 @@ static void panelRegister(ARegionType *region_type) region_type, "influence", "Influence", NULL, influence_panel_draw, panel_type); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) { const WeightVGEditModifierData *wmd = (const WeightVGEditModifierData *)md; + BLO_write_struct(writer, WeightVGEditModifierData, wmd); + if (wmd->cmap_curve) { BKE_curvemapping_blend_write(writer, wmd->cmap_curve); } diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index 43a90b2a4ac..1bea5b93c97 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -715,10 +715,12 @@ static void panelRegister(ARegionType *region_type) region_type, "influence", "Influence", NULL, influence_panel_draw, panel_type); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) { const WeightVGProximityModifierData *wmd = (const WeightVGProximityModifierData *)md; + BLO_write_struct(writer, WeightVGProximityModifierData, wmd); + if (wmd->cmap_curve) { BKE_curvemapping_blend_write(writer, wmd->cmap_curve); } diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h index 3d3450d9252..5d782674f16 100644 --- a/source/blender/nodes/NOD_composite.h +++ b/source/blender/nodes/NOD_composite.h @@ -126,6 +126,8 @@ void register_node_type_cmp_planetrackdeform(void); void register_node_type_cmp_cornerpin(void); void register_node_type_cmp_separate_xyz(void); void register_node_type_cmp_combine_xyz(void); +void register_node_type_cmp_separate_color(void); +void register_node_type_cmp_combine_color(void); void node_cmp_rlayers_outputs(struct bNodeTree *ntree, struct bNode *node); void node_cmp_rlayers_register_pass(struct bNodeTree *ntree, diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index cde4b67e120..ad96fba1929 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -8,6 +8,7 @@ extern "C" { void register_node_type_fn_align_euler_to_vector(void); void register_node_type_fn_boolean_math(void); +void register_node_type_fn_combine_color(void); void register_node_type_fn_compare(void); void register_node_type_fn_float_to_int(void); void register_node_type_fn_input_bool(void); @@ -19,6 +20,7 @@ void register_node_type_fn_input_vector(void); void register_node_type_fn_random_value(void); void register_node_type_fn_replace_string(void); void register_node_type_fn_rotate_euler(void); +void register_node_type_fn_separate_color(void); void register_node_type_fn_slice_string(void); void register_node_type_fn_string_length(void); void register_node_type_fn_value_to_string(void); diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index 1ad859aa47b..4fbf5192222 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -103,16 +103,16 @@ class GeometryValueLog : public ValueLog { public: struct MeshInfo { - int tot_verts, tot_edges, tot_faces; + int verts_num, edges_num, faces_num; }; struct CurveInfo { - int tot_splines; + int splines_num; }; struct PointCloudInfo { - int tot_points; + int points_num; }; struct InstancesInfo { - int tot_instances; + int instances_num; }; std::optional<MeshInfo> mesh_info; diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h index 4996f12e27d..1d1310360b8 100644 --- a/source/blender/nodes/NOD_shader.h +++ b/source/blender/nodes/NOD_shader.h @@ -43,6 +43,8 @@ void register_node_type_sh_vect_math(void); void register_node_type_sh_squeeze(void); void register_node_type_sh_dynamic(void); void register_node_type_sh_invert(void); +void register_node_type_sh_sepcolor(void); +void register_node_type_sh_combcolor(void); void register_node_type_sh_seprgb(void); void register_node_type_sh_combrgb(void); void register_node_type_sh_sephsv(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 5818fa631e7..7dd732e7fad 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -42,8 +42,8 @@ DefNode(ShaderNode, SH_NODE_MATH, def_math, "MATH", DefNode(ShaderNode, SH_NODE_VECTOR_MATH, def_vector_math, "VECT_MATH", VectorMath, "Vector Math", "" ) DefNode(ShaderNode, SH_NODE_SQUEEZE, 0, "SQUEEZE", Squeeze, "Squeeze Value", "" ) DefNode(ShaderNode, SH_NODE_INVERT, 0, "INVERT", Invert, "Invert", "" ) -DefNode(ShaderNode, SH_NODE_SEPRGB, 0, "SEPRGB", SeparateRGB, "Separate RGB", "" ) -DefNode(ShaderNode, SH_NODE_COMBRGB, 0, "COMBRGB", CombineRGB, "Combine RGB", "" ) +DefNode(ShaderNode, SH_NODE_SEPRGB_LEGACY, 0, "SEPRGB", SeparateRGB, "Separate RGB", "" ) +DefNode(ShaderNode, SH_NODE_COMBRGB_LEGACY, 0, "COMBRGB", CombineRGB, "Combine RGB", "" ) DefNode(ShaderNode, SH_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue/Saturation", "" ) DefNode(ShaderNode, SH_NODE_OUTPUT_MATERIAL, def_sh_output, "OUTPUT_MATERIAL", OutputMaterial, "Material Output", "" ) @@ -106,8 +106,8 @@ DefNode(ShaderNode, SH_NODE_TEX_POINTDENSITY, def_sh_tex_pointdensity,"TEX DefNode(ShaderNode, SH_NODE_TEX_COORD, def_sh_tex_coord, "TEX_COORD", TexCoord, "Texture Coordinate","" ) DefNode(ShaderNode, SH_NODE_VECTOR_ROTATE, def_sh_vector_rotate, "VECTOR_ROTATE", VectorRotate, "Vector Rotate", "" ) DefNode(ShaderNode, SH_NODE_VECT_TRANSFORM, def_sh_vect_transform, "VECT_TRANSFORM", VectorTransform, "Vector Transform", "" ) -DefNode(ShaderNode, SH_NODE_SEPHSV, 0, "SEPHSV", SeparateHSV, "Separate HSV", "" ) -DefNode(ShaderNode, SH_NODE_COMBHSV, 0, "COMBHSV", CombineHSV, "Combine HSV", "" ) +DefNode(ShaderNode, SH_NODE_SEPHSV_LEGACY, 0, "SEPHSV", SeparateHSV, "Separate HSV", "" ) +DefNode(ShaderNode, SH_NODE_COMBHSV_LEGACY, 0, "COMBHSV", CombineHSV, "Combine HSV", "" ) DefNode(ShaderNode, SH_NODE_UVMAP, def_sh_uvmap, "UVMAP", UVMap, "UV Map", "" ) DefNode(ShaderNode, SH_NODE_VERTEX_COLOR, def_sh_vertex_color, "VERTEX_COLOR", VertexColor, "Color Attribute", "" ) DefNode(ShaderNode, SH_NODE_UVALONGSTROKE, def_sh_uvalongstroke, "UVALONGSTROKE", UVAlongStroke, "UV Along Stroke", "" ) @@ -120,6 +120,8 @@ DefNode(ShaderNode, SH_NODE_TEX_IES, def_sh_tex_ies, "TEX DefNode(ShaderNode, SH_NODE_TEX_WHITE_NOISE, def_sh_tex_white_noise, "TEX_WHITE_NOISE", TexWhiteNoise, "White Noise", "" ) DefNode(ShaderNode, SH_NODE_OUTPUT_AOV, def_sh_output_aov, "OUTPUT_AOV", OutputAOV, "AOV Output", "" ) DefNode(ShaderNode, SH_NODE_CURVE_FLOAT, def_float_curve, "CURVE_FLOAT", FloatCurve, "Float Curve", "" ) +DefNode(ShaderNode, SH_NODE_COMBINE_COLOR, def_sh_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "" ) +DefNode(ShaderNode, SH_NODE_SEPARATE_COLOR, def_sh_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "" ) DefNode(CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" ) DefNode(CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" ) @@ -137,8 +139,8 @@ DefNode(CompositorNode, CMP_NODE_MAP_VALUE, def_cmp_map_value, "MAP_VA DefNode(CompositorNode, CMP_NODE_MAP_RANGE, def_cmp_map_range, "MAP_RANGE", MapRange, "Map Range", "" ) DefNode(CompositorNode, CMP_NODE_TIME, def_time, "TIME", Time, "Time Curve", "" ) DefNode(CompositorNode, CMP_NODE_VECBLUR, def_cmp_vector_blur, "VECBLUR", VecBlur, "Vector Blur", "" ) -DefNode(CompositorNode, CMP_NODE_SEPRGBA, 0, "SEPRGBA", SepRGBA, "Separate RGBA", "" ) -DefNode(CompositorNode, CMP_NODE_SEPHSVA, 0, "SEPHSVA", SepHSVA, "Separate HSVA", "" ) +DefNode(CompositorNode, CMP_NODE_SEPRGBA_LEGACY, 0, "SEPRGBA", SepRGBA, "Separate RGBA", "" ) +DefNode(CompositorNode, CMP_NODE_SEPHSVA_LEGACY, 0, "SEPHSVA", SepHSVA, "Separate HSVA", "" ) DefNode(CompositorNode, CMP_NODE_SETALPHA, def_cmp_set_alpha, "SETALPHA", SetAlpha, "Set Alpha", "" ) DefNode(CompositorNode, CMP_NODE_HUE_SAT, 0, "HUE_SAT", HueSat, "Hue Saturation Value","" ) DefNode(CompositorNode, CMP_NODE_IMAGE, def_cmp_image, "IMAGE", Image, "Image", "" ) @@ -149,16 +151,16 @@ DefNode(CompositorNode, CMP_NODE_OUTPUT_FILE, 0, "OUTPUT DefNode(CompositorNode, CMP_NODE_TEXTURE, def_texture, "TEXTURE", Texture, "Texture", "" ) DefNode(CompositorNode, CMP_NODE_TRANSLATE, def_cmp_translate, "TRANSLATE", Translate, "Translate", "" ) DefNode(CompositorNode, CMP_NODE_ZCOMBINE, def_cmp_zcombine, "ZCOMBINE", Zcombine, "Z Combine", "" ) -DefNode(CompositorNode, CMP_NODE_COMBRGBA, 0, "COMBRGBA", CombRGBA, "Combine RGBA", "" ) +DefNode(CompositorNode, CMP_NODE_COMBRGBA_LEGACY,0, "COMBRGBA", CombRGBA, "Combine RGBA", "" ) DefNode(CompositorNode, CMP_NODE_DILATEERODE, def_cmp_dilate_erode, "DILATEERODE", DilateErode, "Dilate/Erode", "" ) DefNode(CompositorNode, CMP_NODE_INPAINT, def_cmp_inpaint, "INPAINT", Inpaint, "Inpaint", "" ) DefNode(CompositorNode, CMP_NODE_DESPECKLE, def_cmp_despeckle, "DESPECKLE", Despeckle, "Despeckle", "" ) DefNode(CompositorNode, CMP_NODE_ROTATE, def_cmp_rotate, "ROTATE", Rotate, "Rotate", "" ) DefNode(CompositorNode, CMP_NODE_SCALE, def_cmp_scale, "SCALE", Scale, "Scale", "" ) -DefNode(CompositorNode, CMP_NODE_SEPYCCA, def_cmp_ycc, "SEPYCCA", SepYCCA, "Separate YCbCrA", "" ) -DefNode(CompositorNode, CMP_NODE_COMBYCCA, def_cmp_ycc, "COMBYCCA", CombYCCA, "Combine YCbCrA", "" ) -DefNode(CompositorNode, CMP_NODE_SEPYUVA, 0, "SEPYUVA", SepYUVA, "Separate YUVA", "" ) -DefNode(CompositorNode, CMP_NODE_COMBYUVA, 0, "COMBYUVA", CombYUVA, "Combine YUVA", "" ) +DefNode(CompositorNode, CMP_NODE_SEPYCCA_LEGACY, def_cmp_ycc, "SEPYCCA", SepYCCA, "Separate YCbCrA", "" ) +DefNode(CompositorNode, CMP_NODE_COMBYCCA_LEGACY,def_cmp_ycc, "COMBYCCA", CombYCCA, "Combine YCbCrA", "" ) +DefNode(CompositorNode, CMP_NODE_SEPYUVA_LEGACY, 0, "SEPYUVA", SepYUVA, "Separate YUVA", "" ) +DefNode(CompositorNode, CMP_NODE_COMBYUVA_LEGACY,0, "COMBYUVA", CombYUVA, "Combine YUVA", "" ) DefNode(CompositorNode, CMP_NODE_DIFF_MATTE, def_cmp_diff_matte, "DIFF_MATTE", DiffMatte, "Difference Key", "" ) DefNode(CompositorNode, CMP_NODE_COLOR_SPILL, def_cmp_color_spill, "COLOR_SPILL", ColorSpill, "Color Spill", "" ) DefNode(CompositorNode, CMP_NODE_CHROMA_MATTE, def_cmp_chroma_matte, "CHROMA_MATTE", ChromaMatte, "Chroma Key", "" ) @@ -170,7 +172,7 @@ DefNode(CompositorNode, CMP_NODE_ID_MASK, def_cmp_id_mask, "ID_MAS DefNode(CompositorNode, CMP_NODE_DOUBLEEDGEMASK, def_cmp_double_edge_mask,"DOUBLEEDGEMASK", DoubleEdgeMask, "Double Edge Mask", "" ) DefNode(CompositorNode, CMP_NODE_DEFOCUS, def_cmp_defocus, "DEFOCUS", Defocus, "Defocus", "" ) DefNode(CompositorNode, CMP_NODE_DISPLACE, 0, "DISPLACE", Displace, "Displace", "" ) -DefNode(CompositorNode, CMP_NODE_COMBHSVA, 0, "COMBHSVA", CombHSVA, "Combine HSVA", "" ) +DefNode(CompositorNode, CMP_NODE_COMBHSVA_LEGACY,0, "COMBHSVA", CombHSVA, "Combine HSVA", "" ) DefNode(CompositorNode, CMP_NODE_MATH, def_math, "MATH", Math, "Math", "" ) DefNode(CompositorNode, CMP_NODE_LUMA_MATTE, def_cmp_luma_matte, "LUMA_MATTE", LumaMatte, "Luminance Key", "" ) DefNode(CompositorNode, CMP_NODE_BRIGHTCONTRAST, def_cmp_brightcontrast, "BRIGHTCONTRAST", BrightContrast, "Bright/Contrast", "" ) @@ -215,9 +217,11 @@ DefNode(CompositorNode, CMP_NODE_EXPOSURE, 0, "EXPOSU DefNode(CompositorNode, CMP_NODE_ANTIALIASING, def_cmp_antialiasing, "ANTIALIASING", AntiAliasing, "Anti-Aliasing", "" ) DefNode(CompositorNode, CMP_NODE_POSTERIZE, 0, "POSTERIZE", Posterize, "Posterize", "" ) DefNode(CompositorNode, CMP_NODE_CONVERT_COLOR_SPACE,def_cmp_convert_color_space, "CONVERT_COLORSPACE", ConvertColorSpace, "Color Space","" ) -DefNode(CompositorNode, CMP_NODE_SCENE_TIME, 0, "SCENE_TIME", SceneTime, "Scene Time", "" ) +DefNode(CompositorNode, CMP_NODE_SCENE_TIME, 0, "SCENE_TIME", SceneTime, "Scene Time", "" ) DefNode(CompositorNode, CMP_NODE_COMBINE_XYZ, 0, "COMBINE_XYZ", CombineXYZ, "Combine XYZ", "" ) DefNode(CompositorNode, CMP_NODE_SEPARATE_XYZ, 0, "SEPARATE_XYZ", SeparateXYZ, "Separate XYZ", "" ) +DefNode(CompositorNode, CMP_NODE_SEPARATE_COLOR, def_cmp_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "" ) +DefNode(CompositorNode, CMP_NODE_COMBINE_COLOR, def_cmp_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "" ) DefNode(TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" ) DefNode(TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" ) @@ -237,11 +241,13 @@ DefNode(TextureNode, TEX_NODE_VIEWER, 0, "VIEWER DefNode(TextureNode, TEX_NODE_TRANSLATE, 0, "TRANSLATE", Translate, "Translate", "" ) DefNode(TextureNode, TEX_NODE_COORD, 0, "COORD", Coordinates, "Coordinates", "" ) DefNode(TextureNode, TEX_NODE_DISTANCE, 0, "DISTANCE", Distance, "Distance", "" ) -DefNode(TextureNode, TEX_NODE_COMPOSE, 0, "COMPOSE", Compose, "Combine RGBA", "" ) -DefNode(TextureNode, TEX_NODE_DECOMPOSE, 0, "DECOMPOSE", Decompose, "Separate RGBA", "" ) +DefNode(TextureNode, TEX_NODE_COMPOSE_LEGACY, 0, "COMPOSE", Compose, "Combine RGBA", "" ) +DefNode(TextureNode, TEX_NODE_DECOMPOSE_LEGACY,0, "DECOMPOSE", Decompose, "Separate RGBA", "" ) DefNode(TextureNode, TEX_NODE_VALTONOR, 0, "VALTONOR", ValToNor, "Value to Normal", "" ) DefNode(TextureNode, TEX_NODE_SCALE, 0, "SCALE", Scale, "Scale", "" ) DefNode(TextureNode, TEX_NODE_AT, 0, "AT", At, "At", "" ) +DefNode(TextureNode, TEX_NODE_COMBINE_COLOR, def_tex_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "" ) +DefNode(TextureNode, TEX_NODE_SEPARATE_COLOR, def_tex_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "" ) /* procedural textures */ DefNode(TextureNode, TEX_NODE_PROC+TEX_VORONOI, 0, "TEX_VORONOI", TexVoronoi, "Voronoi", "" ) DefNode(TextureNode, TEX_NODE_PROC+TEX_BLEND, 0, "TEX_BLEND", TexBlend, "Blend", "" ) @@ -256,6 +262,7 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DI DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler To Vector", "") DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") +DefNode(FunctionNode, FN_NODE_COMBINE_COLOR, def_fn_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "") DefNode(FunctionNode, FN_NODE_COMPARE, def_compare, "COMPARE", Compare, "Compare", "") DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "") DefNode(FunctionNode, FN_NODE_INPUT_BOOL, def_fn_input_bool, "INPUT_BOOL", InputBool, "Boolean", "") @@ -267,6 +274,7 @@ DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", DefNode(FunctionNode, FN_NODE_RANDOM_VALUE, def_fn_random_value, "RANDOM_VALUE", RandomValue, "Random Value", "") DefNode(FunctionNode, FN_NODE_REPLACE_STRING, 0, "REPLACE_STRING", ReplaceString, "Replace String", "") DefNode(FunctionNode, FN_NODE_ROTATE_EULER, def_fn_rotate_euler, "ROTATE_EULER", RotateEuler, "Rotate Euler", "") +DefNode(FunctionNode, FN_NODE_SEPARATE_COLOR, def_fn_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "") DefNode(FunctionNode, FN_NODE_SLICE_STRING, 0, "SLICE_STRING", SliceString, "Slice String", "") DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "") DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "") diff --git a/source/blender/nodes/NOD_texture.h b/source/blender/nodes/NOD_texture.h index 0f07b17f165..9a2dc705c0d 100644 --- a/source/blender/nodes/NOD_texture.h +++ b/source/blender/nodes/NOD_texture.h @@ -46,6 +46,8 @@ void register_node_type_tex_at(void); void register_node_type_tex_compose(void); void register_node_type_tex_decompose(void); +void register_node_type_tex_combine_color(void); +void register_node_type_tex_separate_color(void); void register_node_type_tex_proc_voronoi(void); void register_node_type_tex_proc_blend(void); diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt index 57f76f20ac6..c0100d77889 100644 --- a/source/blender/nodes/composite/CMakeLists.txt +++ b/source/blender/nodes/composite/CMakeLists.txt @@ -91,6 +91,7 @@ set(SRC nodes/node_composite_rotate.cc nodes/node_composite_scale.cc nodes/node_composite_scene_time.cc + nodes/node_composite_sepcomb_color.cc nodes/node_composite_sepcomb_hsva.cc nodes/node_composite_sepcomb_rgba.cc nodes/node_composite_sepcomb_xyz.cc diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc new file mode 100644 index 00000000000..b253656a628 --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_composite_util.hh" + +static void node_cmp_combsep_color_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeCMPCombSepColor *data = MEM_cnew<NodeCMPCombSepColor>(__func__); + data->mode = CMP_NODE_COMBSEP_COLOR_RGB; + data->ycc_mode = BLI_YCC_ITU_BT709; + node->storage = data; +} + +static void node_cmp_combsep_color_label(const ListBase *sockets, CMPNodeCombSepColorMode mode) +{ + bNodeSocket *sock1 = (bNodeSocket *)sockets->first; + bNodeSocket *sock2 = sock1->next; + bNodeSocket *sock3 = sock2->next; + + node_sock_label_clear(sock1); + node_sock_label_clear(sock2); + node_sock_label_clear(sock3); + + switch (mode) { + case CMP_NODE_COMBSEP_COLOR_RGB: + node_sock_label(sock1, "Red"); + node_sock_label(sock2, "Green"); + node_sock_label(sock3, "Blue"); + break; + case CMP_NODE_COMBSEP_COLOR_HSV: + node_sock_label(sock1, "Hue"); + node_sock_label(sock2, "Saturation"); + node_sock_label(sock3, "Value"); + break; + case CMP_NODE_COMBSEP_COLOR_HSL: + node_sock_label(sock1, "Hue"); + node_sock_label(sock2, "Saturation"); + node_sock_label(sock3, "Lightness"); + break; + case CMP_NODE_COMBSEP_COLOR_YCC: + node_sock_label(sock1, "Y"); + node_sock_label(sock2, "Cb"); + node_sock_label(sock3, "Cr"); + break; + case CMP_NODE_COMBSEP_COLOR_YUV: + node_sock_label(sock1, "Y"); + node_sock_label(sock2, "U"); + node_sock_label(sock3, "V"); + break; + default: + BLI_assert_unreachable(); + break; + } +} + +/* **************** SEPARATE COLOR ******************** */ + +namespace blender::nodes::node_composite_separate_color_cc { + +static void cmp_node_separate_color_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Float>(N_("Red")); + b.add_output<decl::Float>(N_("Green")); + b.add_output<decl::Float>(N_("Blue")); + b.add_output<decl::Float>(N_("Alpha")); +} + +static void cmp_node_separate_color_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)node->storage; + node_cmp_combsep_color_label(&node->outputs, (CMPNodeCombSepColorMode)storage->mode); +} + +} // namespace blender::nodes::node_composite_separate_color_cc + +void register_node_type_cmp_separate_color() +{ + namespace file_ns = blender::nodes::node_composite_separate_color_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_separate_color_declare; + node_type_init(&ntype, node_cmp_combsep_color_init); + node_type_storage( + &ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage); + node_type_update(&ntype, file_ns::cmp_node_separate_color_update); + + nodeRegisterType(&ntype); +} + +/* **************** COMBINE COLOR ******************** */ + +namespace blender::nodes::node_composite_combine_color_cc { + +static void cmp_node_combine_color_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Green")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Blue")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Alpha")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_output<decl::Color>(N_("Image")); +} + +static void cmp_node_combine_color_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)node->storage; + node_cmp_combsep_color_label(&node->inputs, (CMPNodeCombSepColorMode)storage->mode); +} + +} // namespace blender::nodes::node_composite_combine_color_cc + +void register_node_type_cmp_combine_color() +{ + namespace file_ns = blender::nodes::node_composite_combine_color_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_combine_color_declare; + node_type_init(&ntype, node_cmp_combsep_color_init); + node_type_storage( + &ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage); + node_type_update(&ntype, file_ns::cmp_node_combine_color_update); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc index 349c27d876d..a0d2485ea5a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc @@ -28,7 +28,7 @@ void register_node_type_cmp_sephsva() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA, "Separate HSVA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA_LEGACY, "Separate HSVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sephsva_declare; nodeRegisterType(&ntype); } @@ -54,7 +54,7 @@ void register_node_type_cmp_combhsva() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA, "Combine HSVA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA_LEGACY, "Combine HSVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combhsva_declare; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc index c46603be847..ae46681b0f4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc @@ -27,7 +27,7 @@ void register_node_type_cmp_seprgba() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA, "Separate RGBA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA_LEGACY, "Separate RGBA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_seprgba_declare; nodeRegisterType(&ntype); @@ -54,7 +54,7 @@ void register_node_type_cmp_combrgba() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA, "Combine RGBA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA_LEGACY, "Combine RGBA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combrgba_declare; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc index 9b5c153fddf..a3c40b61e64 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc @@ -33,7 +33,7 @@ void register_node_type_cmp_sepycca() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA, "Separate YCbCrA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA_LEGACY, "Separate YCbCrA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sepycca_declare; node_type_init(&ntype, file_ns::node_composit_init_mode_sepycca); @@ -66,7 +66,7 @@ void register_node_type_cmp_combycca() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA, "Combine YCbCrA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA_LEGACY, "Combine YCbCrA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combycca_declare; node_type_init(&ntype, file_ns::node_composit_init_mode_combycca); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc index e458c9cfb7e..7fdece5904d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc @@ -28,7 +28,7 @@ void register_node_type_cmp_sepyuva() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA, "Separate YUVA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA_LEGACY, "Separate YUVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sepyuva_declare; nodeRegisterType(&ntype); @@ -55,7 +55,7 @@ void register_node_type_cmp_combyuva() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA, "Combine YUVA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA_LEGACY, "Combine YUVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combyuva_declare; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/function/CMakeLists.txt b/source/blender/nodes/function/CMakeLists.txt index 6ccc4c7bf5c..d03f1cb63ff 100644 --- a/source/blender/nodes/function/CMakeLists.txt +++ b/source/blender/nodes/function/CMakeLists.txt @@ -20,6 +20,7 @@ set(INC set(SRC nodes/node_fn_align_euler_to_vector.cc nodes/node_fn_boolean_math.cc + nodes/node_fn_combine_color.cc nodes/node_fn_compare.cc nodes/node_fn_float_to_int.cc nodes/node_fn_input_bool.cc @@ -31,6 +32,7 @@ set(SRC nodes/node_fn_random_value.cc nodes/node_fn_replace_string.cc nodes/node_fn_rotate_euler.cc + nodes/node_fn_separate_color.cc nodes/node_fn_slice_string.cc nodes/node_fn_string_length.cc nodes/node_fn_value_to_string.cc diff --git a/source/blender/nodes/function/nodes/node_fn_combine_color.cc b/source/blender/nodes/function/nodes/node_fn_combine_color.cc new file mode 100644 index 00000000000..c5fd3ce38a1 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_combine_color.cc @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_function_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +NODE_STORAGE_FUNCS(NodeCombSepColor) + +static void fn_node_combine_color_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Green")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Blue")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Alpha")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_output<decl::Color>(N_("Color")); +}; + +static void fn_node_combine_color_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void fn_node_combine_color_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCombSepColor &storage = node_storage(*node); + node_combsep_color_label(&node->inputs, (NodeCombSepColorMode)storage.mode); +} + +static void fn_node_combine_color_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeCombSepColor *data = MEM_cnew<NodeCombSepColor>(__func__); + data->mode = NODE_COMBSEP_COLOR_RGB; + node->storage = data; +} + +static const fn::MultiFunction *get_multi_function(bNode &bnode) +{ + const NodeCombSepColor &storage = node_storage(bnode); + + static fn::CustomMF_SI_SI_SI_SI_SO<float, float, float, float, ColorGeometry4f> rgba_fn{ + "RGB", [](float r, float g, float b, float a) { return ColorGeometry4f(r, g, b, a); }}; + static fn::CustomMF_SI_SI_SI_SI_SO<float, float, float, float, ColorGeometry4f> hsva_fn{ + "HSV", [](float h, float s, float v, float a) { + ColorGeometry4f r_color; + hsv_to_rgb(h, s, v, &r_color.r, &r_color.g, &r_color.b); + r_color.a = a; + return r_color; + }}; + static fn::CustomMF_SI_SI_SI_SI_SO<float, float, float, float, ColorGeometry4f> hsla_fn{ + "HSL", [](float h, float s, float l, float a) { + ColorGeometry4f color; + hsl_to_rgb(h, s, l, &color.r, &color.g, &color.b); + color.a = a; + return color; + }}; + + switch (storage.mode) { + case NODE_COMBSEP_COLOR_RGB: + return &rgba_fn; + case NODE_COMBSEP_COLOR_HSV: + return &hsva_fn; + case NODE_COMBSEP_COLOR_HSL: + return &hsla_fn; + } + + BLI_assert_unreachable(); + return nullptr; +} + +static void fn_node_combine_color_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + const fn::MultiFunction *fn = get_multi_function(builder.node()); + builder.set_matching_fn(fn); +} + +} // namespace blender::nodes + +void register_node_type_fn_combine_color(void) +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_CONVERTER); + ntype.declare = blender::nodes::fn_node_combine_color_declare; + node_type_update(&ntype, blender::nodes::fn_node_combine_color_update); + node_type_init(&ntype, blender::nodes::fn_node_combine_color_init); + node_type_storage( + &ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = blender::nodes::fn_node_combine_color_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_combine_color_layout; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_separate_color.cc b/source/blender/nodes/function/nodes/node_fn_separate_color.cc new file mode 100644 index 00000000000..1701dfdc6fa --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_separate_color.cc @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_function_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +NODE_STORAGE_FUNCS(NodeCombSepColor) + +static void fn_node_separate_color_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Float>(N_("Red")); + b.add_output<decl::Float>(N_("Green")); + b.add_output<decl::Float>(N_("Blue")); + b.add_output<decl::Float>(N_("Alpha")); +}; + +static void fn_node_separate_color_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void fn_node_separate_color_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCombSepColor &storage = node_storage(*node); + node_combsep_color_label(&node->outputs, (NodeCombSepColorMode)storage.mode); +} + +static void fn_node_separate_color_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeCombSepColor *data = MEM_cnew<NodeCombSepColor>(__func__); + data->mode = NODE_COMBSEP_COLOR_RGB; + node->storage = data; +} + +class SeparateRGBAFunction : public fn::MultiFunction { + public: + SeparateRGBAFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Separate Color"}; + signature.single_input<ColorGeometry4f>("Color"); + signature.single_output<float>("Red"); + signature.single_output<float>("Green"); + signature.single_output<float>("Blue"); + signature.single_output<float>("Alpha"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<ColorGeometry4f> &colors = params.readonly_single_input<ColorGeometry4f>(0, + "Color"); + MutableSpan<float> red = params.uninitialized_single_output<float>(1, "Red"); + MutableSpan<float> green = params.uninitialized_single_output<float>(2, "Green"); + MutableSpan<float> blue = params.uninitialized_single_output<float>(3, "Blue"); + MutableSpan<float> alpha = params.uninitialized_single_output_if_required<float>(4, "Alpha"); + + for (int64_t i : mask) { + red[i] = colors[i].r; + green[i] = colors[i].g; + blue[i] = colors[i].b; + } + + if (!alpha.is_empty()) { + for (int64_t i : mask) { + alpha[i] = colors[i].a; + } + } + } +}; + +class SeparateHSVAFunction : public fn::MultiFunction { + public: + SeparateHSVAFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Separate Color"}; + signature.single_input<ColorGeometry4f>("Color"); + signature.single_output<float>("Hue"); + signature.single_output<float>("Saturation"); + signature.single_output<float>("Value"); + signature.single_output<float>("Alpha"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<ColorGeometry4f> &colors = params.readonly_single_input<ColorGeometry4f>(0, + "Color"); + MutableSpan<float> hue = params.uninitialized_single_output<float>(1, "Hue"); + MutableSpan<float> saturation = params.uninitialized_single_output<float>(2, "Saturation"); + MutableSpan<float> value = params.uninitialized_single_output<float>(3, "Value"); + MutableSpan<float> alpha = params.uninitialized_single_output_if_required<float>(4, "Alpha"); + + for (int64_t i : mask) { + rgb_to_hsv(colors[i].r, colors[i].g, colors[i].b, &hue[i], &saturation[i], &value[i]); + } + + if (!alpha.is_empty()) { + for (int64_t i : mask) { + alpha[i] = colors[i].a; + } + } + } +}; + +class SeparateHSLAFunction : public fn::MultiFunction { + public: + SeparateHSLAFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Separate Color"}; + signature.single_input<ColorGeometry4f>("Color"); + signature.single_output<float>("Hue"); + signature.single_output<float>("Saturation"); + signature.single_output<float>("Lightness"); + signature.single_output<float>("Alpha"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<ColorGeometry4f> &colors = params.readonly_single_input<ColorGeometry4f>(0, + "Color"); + MutableSpan<float> hue = params.uninitialized_single_output<float>(1, "Hue"); + MutableSpan<float> saturation = params.uninitialized_single_output<float>(2, "Saturation"); + MutableSpan<float> lightness = params.uninitialized_single_output<float>(3, "Lightness"); + MutableSpan<float> alpha = params.uninitialized_single_output_if_required<float>(4, "Alpha"); + + for (int64_t i : mask) { + rgb_to_hsl(colors[i].r, colors[i].g, colors[i].b, &hue[i], &saturation[i], &lightness[i]); + } + + if (!alpha.is_empty()) { + for (int64_t i : mask) { + alpha[i] = colors[i].a; + } + } + } +}; + +static void fn_node_separate_color_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + const NodeCombSepColor &storage = node_storage(builder.node()); + + switch (storage.mode) { + case NODE_COMBSEP_COLOR_RGB: { + static SeparateRGBAFunction fn; + builder.set_matching_fn(fn); + break; + } + case NODE_COMBSEP_COLOR_HSV: { + static SeparateHSVAFunction fn; + builder.set_matching_fn(fn); + break; + } + case NODE_COMBSEP_COLOR_HSL: { + static SeparateHSLAFunction fn; + builder.set_matching_fn(fn); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } +} + +} // namespace blender::nodes + +void register_node_type_fn_separate_color(void) +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_CONVERTER); + ntype.declare = blender::nodes::fn_node_separate_color_declare; + node_type_update(&ntype, blender::nodes::fn_node_separate_color_update); + node_type_init(&ntype, blender::nodes::fn_node_separate_color_init); + node_type_storage( + &ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = blender::nodes::fn_node_separate_color_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_separate_color_layout; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index e081e007c81..38e914b9a9f 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -110,7 +110,7 @@ void register_node_tree_type_geo() tt->type = NTREE_GEOMETRY; strcpy(tt->idname, "GeometryNodeTree"); strcpy(tt->ui_name, N_("Geometry Node Editor")); - tt->ui_icon = ICON_NODETREE; + tt->ui_icon = ICON_GEOMETRY_NODES; strcpy(tt->ui_description, N_("Geometry nodes")); tt->rna_ext.srna = &RNA_GeometryNodeTree; tt->update = geometry_node_tree_update; diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 7af3159bbf8..8f20da66c3b 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -57,8 +57,6 @@ Mesh *create_cylinder_or_cone_mesh(float radius_top, GeometryNodeMeshCircleFillType fill_type, ConeAttributeOutputs &attribute_outputs); -Mesh *create_cuboid_mesh(float3 size, int verts_x, int verts_y, int verts_z); - /** * Copies the point domain attributes from `in_component` that are in the mask to `out_component`. */ @@ -81,14 +79,4 @@ void separate_geometry(GeometrySet &geometry_set, std::optional<CustomDataType> node_data_type_to_custom_data_type(eNodeSocketDatatype type); std::optional<CustomDataType> node_socket_to_custom_data_type(const bNodeSocket &socket); -class SplineLengthFieldInput final : public GeometryFieldInput { - public: - SplineLengthFieldInput(); - GVArray get_varray_for_context(const GeometryComponent &component, - AttributeDomain domain, - IndexMask mask) const final; - uint64_t hash() const override; - bool is_equal_to(const fn::FieldNode &other) const override; -}; - } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc index ea26eec0c15..b29831ceeb6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc @@ -217,16 +217,16 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu IndexMask UNUSED(mask)) const final { const GeometryComponentFieldContext field_context{component, source_domain_}; - const int domain_size = component.attribute_domain_size(field_context.domain()); + const int domain_num = component.attribute_domain_num(field_context.domain()); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(input_); evaluator.add(group_index_); evaluator.evaluate(); const VArray<T> &values = evaluator.get_evaluated<T>(0); const VArray<int> &group_indices = evaluator.get_evaluated<int>(1); - Array<T> accumulations_out(domain_size); + Array<T> accumulations_out(domain_num); if (group_indices.is_single()) { T accumulation = T(); @@ -303,9 +303,9 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput { IndexMask UNUSED(mask)) const final { const GeometryComponentFieldContext field_context{component, source_domain_}; - const int domain_size = component.attribute_domain_size(field_context.domain()); + const int domain_num = component.attribute_domain_num(field_context.domain()); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(input_); evaluator.add(group_index_); evaluator.evaluate(); @@ -317,10 +317,10 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput { for (const int i : values.index_range()) { accumulation = values[i] + accumulation; } - return VArray<T>::ForSingle(accumulation, domain_size); + return VArray<T>::ForSingle(accumulation, domain_num); } - Array<T> accumulations_out(domain_size); + Array<T> accumulations_out(domain_num); Map<int, T> accumulations; for (const int i : values.index_range()) { T &value = accumulations.lookup_or_add_default(group_indices[i]); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index 45a6aabeb03..18cf005c965 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -112,8 +112,8 @@ static void try_capture_field_on_geometry(GeometryComponent &component, const GField &field) { GeometryComponentFieldContext field_context{component, domain}; - const int domain_size = component.attribute_domain_size(domain); - const IndexMask mask{IndexMask(domain_size)}; + const int domain_num = component.attribute_domain_num(domain); + const IndexMask mask{IndexMask(domain_num)}; const CustomDataType data_type = bke::cpp_type_to_custom_data_type(field.cpp_type()); OutputAttribute output_attribute = component.attribute_try_get_for_output_only( diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc index b3fe9d160b3..8ab0eb678e7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc @@ -72,11 +72,11 @@ static void node_geo_exec(GeoNodeExecParams params) case GEO_COMPONENT_TYPE_MESH: { if (geometry_set.has_mesh()) { const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>(); - params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); - params.set_output("Edge Count", component->attribute_domain_size(ATTR_DOMAIN_EDGE)); - params.set_output("Face Count", component->attribute_domain_size(ATTR_DOMAIN_FACE)); + params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT)); + params.set_output("Edge Count", component->attribute_domain_num(ATTR_DOMAIN_EDGE)); + params.set_output("Face Count", component->attribute_domain_num(ATTR_DOMAIN_FACE)); params.set_output("Face Corner Count", - component->attribute_domain_size(ATTR_DOMAIN_CORNER)); + component->attribute_domain_num(ATTR_DOMAIN_CORNER)); } else { params.set_default_remaining_outputs(); @@ -86,8 +86,8 @@ static void node_geo_exec(GeoNodeExecParams params) case GEO_COMPONENT_TYPE_CURVE: { if (geometry_set.has_curves()) { const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>(); - params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); - params.set_output("Spline Count", component->attribute_domain_size(ATTR_DOMAIN_CURVE)); + params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT)); + params.set_output("Spline Count", component->attribute_domain_num(ATTR_DOMAIN_CURVE)); } else { params.set_default_remaining_outputs(); @@ -98,7 +98,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (geometry_set.has_pointcloud()) { const PointCloudComponent *component = geometry_set.get_component_for_read<PointCloudComponent>(); - params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); + params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT)); } else { params.set_default_remaining_outputs(); @@ -109,8 +109,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (geometry_set.has_instances()) { const InstancesComponent *component = geometry_set.get_component_for_read<InstancesComponent>(); - params.set_output("Instance Count", - component->attribute_domain_size(ATTR_DOMAIN_INSTANCE)); + params.set_output("Instance Count", component->attribute_domain_num(ATTR_DOMAIN_INSTANCE)); } else { params.set_default_remaining_outputs(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc index 1153f18ffd4..c7f65a68d60 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc @@ -197,9 +197,9 @@ static void node_geo_exec(GeoNodeExecParams params) for (const GeometryComponent *component : components) { if (component->attribute_domain_supported(domain)) { GeometryComponentFieldContext field_context{*component, domain}; - const int domain_size = component->attribute_domain_size(domain); + const int domain_num = component->attribute_domain_num(domain); - fn::FieldEvaluator data_evaluator{field_context, domain_size}; + fn::FieldEvaluator data_evaluator{field_context, domain_num}; data_evaluator.add(input_field); data_evaluator.set_selection(selection_field); data_evaluator.evaluate(); @@ -275,9 +275,9 @@ static void node_geo_exec(GeoNodeExecParams params) for (const GeometryComponent *component : components) { if (component->attribute_domain_supported(domain)) { GeometryComponentFieldContext field_context{*component, domain}; - const int domain_size = component->attribute_domain_size(domain); + const int domain_num = component->attribute_domain_num(domain); - fn::FieldEvaluator data_evaluator{field_context, domain_size}; + fn::FieldEvaluator data_evaluator{field_context, domain_num}; data_evaluator.add(input_field); data_evaluator.set_selection(selection_field); data_evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc index 558129fb384..00b10cc8a2f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "GEO_mesh_primitive_cuboid.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_bounding_box_cc { @@ -53,7 +55,7 @@ static void node_geo_exec(GeoNodeExecParams params) else { const float3 scale = sub_max - sub_min; const float3 center = sub_min + scale / 2.0f; - Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2); + Mesh *mesh = geometry::create_cuboid_mesh(scale, 2, 2, 2, "uv_map"); transform_mesh(*mesh, center, float3(0), float3(1)); sub_geometry.replace_mesh(mesh); sub_geometry.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 09d0f13c50d..31f706c497c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -138,14 +138,14 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) { int span_count = 0; int count = 0; - int total_size = 0; + int total_num = 0; Span<float3> positions_span; if (geometry_set.has_mesh()) { count++; const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>(); - total_size += component->attribute_domain_size(ATTR_DOMAIN_POINT); + total_num += component->attribute_domain_num(ATTR_DOMAIN_POINT); } if (geometry_set.has_pointcloud()) { @@ -155,7 +155,7 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) geometry_set.get_component_for_read<PointCloudComponent>(); VArray<float3> varray = component->attribute_get_for_read<float3>( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - total_size += varray.size(); + total_num += varray.size(); positions_span = varray.get_internal_span(); } @@ -165,7 +165,7 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) const Curves &curves_id = *geometry_set.get_curves_for_read(); const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); positions_span = curves.evaluated_positions(); - total_size += positions_span.size(); + total_num += positions_span.size(); } if (count == 0) { @@ -178,7 +178,7 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) return hull_from_bullet(nullptr, positions_span); } - Array<float3> positions(total_size); + Array<float3> positions(total_num); int offset = 0; if (geometry_set.has_mesh()) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 95ea978541c..fb8a488ddae 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -106,13 +106,13 @@ static float3 get_center(const float3 vec_pos2prev, const FilletData &fd, const /* Calculate the direction vectors from each vertex to their previous vertex. */ static Array<float3> calculate_directions(const Span<float3> positions) { - const int size = positions.size(); - Array<float3> directions(size); + const int num = positions.size(); + Array<float3> directions(num); - for (const int i : IndexRange(size - 1)) { + for (const int i : IndexRange(num - 1)) { directions[i] = math::normalize(positions[i + 1] - positions[i]); } - directions[size - 1] = math::normalize(positions[0] - positions[size - 1]); + directions[num - 1] = math::normalize(positions[0] - positions[num - 1]); return directions; } @@ -120,11 +120,11 @@ static Array<float3> calculate_directions(const Span<float3> positions) /* Calculate the axes around which the fillet is built. */ static Array<float3> calculate_axes(const Span<float3> directions) { - const int size = directions.size(); - Array<float3> axes(size); + const int num = directions.size(); + Array<float3> axes(num); - axes[0] = math::normalize(math::cross(-directions[size - 1], directions[0])); - for (const int i : IndexRange(1, size - 1)) { + axes[0] = math::normalize(math::cross(-directions[num - 1], directions[0])); + for (const int i : IndexRange(1, num - 1)) { axes[i] = math::normalize(math::cross(-directions[i - 1], directions[i])); } @@ -134,11 +134,11 @@ static Array<float3> calculate_axes(const Span<float3> directions) /* Calculate the angle of the arc formed by the fillet. */ static Array<float> calculate_angles(const Span<float3> directions) { - const int size = directions.size(); - Array<float> angles(size); + const int num = directions.size(); + Array<float> angles(num); - angles[0] = M_PI - angle_v3v3(-directions[size - 1], directions[0]); - for (const int i : IndexRange(1, size - 1)) { + angles[0] = M_PI - angle_v3v3(-directions[num - 1], directions[0]); + for (const int i : IndexRange(1, num - 1)) { angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]); } @@ -147,18 +147,18 @@ static Array<float> calculate_angles(const Span<float3> directions) /* Calculate the segment count in each filleted arc. */ static Array<int> calculate_counts(const FilletParam &fillet_param, - const int size, + const int num, const int spline_offset, const bool cyclic) { - Array<int> counts(size, 1); + Array<int> counts(num, 1); if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) { - for (const int i : IndexRange(size)) { + for (const int i : IndexRange(num)) { counts[i] = fillet_param.counts[spline_offset + i]; } } if (!cyclic) { - counts[0] = counts[size - 1] = 0; + counts[0] = counts[num - 1] = 0; } return counts; @@ -166,17 +166,17 @@ static Array<int> calculate_counts(const FilletParam &fillet_param, /* Calculate the radii for the vertices to be filleted. */ static Array<float> calculate_radii(const FilletParam &fillet_param, - const int size, + const int num, const int spline_offset) { - Array<float> radii(size, 0.0f); + Array<float> radii(num, 0.0f); if (fillet_param.limit_radius) { - for (const int i : IndexRange(size)) { + for (const int i : IndexRange(num)) { radii[i] = std::max(fillet_param.radii[spline_offset + i], 0.0f); } } else { - for (const int i : IndexRange(size)) { + for (const int i : IndexRange(num)) { radii[i] = fillet_param.radii[spline_offset + i]; } } @@ -207,15 +207,15 @@ static FilletData calculate_fillet_data(const Spline &spline, MutableSpan<int> point_counts, const int spline_offset) { - const int size = spline.size(); + const int num = spline.size(); FilletData fd; fd.directions = calculate_directions(spline.positions()); fd.positions = spline.positions(); fd.axes = calculate_axes(fd.directions); fd.angles = calculate_angles(fd.directions); - fd.counts = calculate_counts(fillet_param, size, spline_offset, spline.is_cyclic()); - fd.radii = calculate_radii(fillet_param, size, spline_offset); + fd.counts = calculate_counts(fillet_param, num, spline_offset, spline.is_cyclic()); + fd.radii = calculate_radii(fillet_param, num, spline_offset); added_count = calculate_point_counts(point_counts, fd.radii, fd.counts); @@ -229,19 +229,19 @@ static void limit_radii(FilletData &fd, const bool cyclic) Span<float> angles(fd.angles); Span<float3> positions(fd.positions); - const int size = radii.size(); - const int fillet_count = cyclic ? size : size - 2; + const int num = radii.size(); + const int fillet_count = cyclic ? num : num - 2; const int start = cyclic ? 0 : 1; - Array<float> max_radii(size, FLT_MAX); + Array<float> max_radii(num, FLT_MAX); if (cyclic) { /* Calculate lengths between adjacent control points. */ - const float len_prev = math::distance(positions[0], positions[size - 1]); + const float len_prev = math::distance(positions[0], positions[num - 1]); const float len_next = math::distance(positions[0], positions[1]); /* Calculate tangent lengths of fillets in control points. */ const float tan_len = radii[0] * tan(angles[0] / 2.0f); - const float tan_len_prev = radii[size - 1] * tan(angles[size - 1] / 2.0f); + const float tan_len_prev = radii[num - 1] * tan(angles[num - 1] / 2.0f); const float tan_len_next = radii[1] * tan(angles[1] / 2.0f); float factor_prev = 1.0f, factor_next = 1.0f; @@ -255,12 +255,12 @@ static void limit_radii(FilletData &fd, const bool cyclic) /* Scale max radii by calculated factors. */ max_radii[0] = radii[0] * std::min(factor_next, factor_prev); max_radii[1] = radii[1] * factor_next; - max_radii[size - 1] = radii[size - 1] * factor_prev; + max_radii[num - 1] = radii[num - 1] * factor_prev; } /* Initialize max_radii to largest possible radii. */ float prev_dist = math::distance(positions[1], positions[0]); - for (const int i : IndexRange(1, size - 2)) { + for (const int i : IndexRange(1, num - 2)) { const float temp_dist = math::distance(positions[i], positions[i + 1]); max_radii[i] = std::min(prev_dist, temp_dist) / tan(angles[i] / 2.0f); prev_dist = temp_dist; @@ -282,7 +282,7 @@ static void limit_radii(FilletData &fd, const bool cyclic) } /* Assign the max_radii to the fillet data's radii. */ - for (const int i : IndexRange(size)) { + for (const int i : IndexRange(num)) { radii[i] = std::min(radii[i], max_radii[i]); } } @@ -358,10 +358,10 @@ static void update_bezier_positions(const FilletData &fd, Span<float3> positions(fd.positions); Span<float3> directions(fd.directions); - const int size = radii.size(); + const int num = radii.size(); int i_dst = 0; - for (const int i_src : IndexRange(size)) { + for (const int i_src : IndexRange(num)) { const int count = point_counts[i_src]; /* Skip if the point count for the vertex is 1. */ @@ -385,7 +385,7 @@ static void update_bezier_positions(const FilletData &fd, /* Position the end points of the arc and their handles. */ const int end_i = i_dst + count - 1; - const float3 prev_dir = i_src == 0 ? -directions[size - 1] : -directions[i_src - 1]; + const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1]; const float3 next_dir = directions[i_src]; dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir; dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir; @@ -442,10 +442,10 @@ static void update_poly_positions(const FilletData &fd, Span<float3> positions(fd.positions); Span<float3> directions(fd.directions); - const int size = radii.size(); + const int num = radii.size(); int i_dst = 0; - for (const int i_src : IndexRange(size)) { + for (const int i_src : IndexRange(num)) { const int count = point_counts[i_src]; /* Skip if the point count for the vertex is 1. */ @@ -460,7 +460,7 @@ static void update_poly_positions(const FilletData &fd, /* Position the end points of the arc. */ const int end_i = i_dst + count - 1; - const float3 prev_dir = i_src == 0 ? -directions[size - 1] : -directions[i_src - 1]; + const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1]; const float3 next_dir = directions[i_src]; dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir; dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir; @@ -487,15 +487,15 @@ static SplinePtr fillet_spline(const Spline &spline, const FilletParam &fillet_param, const int spline_offset) { - const int size = spline.size(); + const int num = spline.size(); const bool cyclic = spline.is_cyclic(); - if (size < 3) { + if (num < 3) { return spline.copy(); } /* Initialize the point_counts with 1s (at least one vertex on dst for each vertex on src). */ - Array<int> point_counts(size, 1); + Array<int> point_counts(num, 1); int added_count = 0; /* Update point_counts array and added_count. */ @@ -505,7 +505,7 @@ static SplinePtr fillet_spline(const Spline &spline, limit_radii(fd, cyclic); } - const int total_points = added_count + size; + const int total_points = added_count + num; const Array<int> dst_to_src = create_dst_to_src_map(point_counts, total_points); SplinePtr dst_spline_ptr = spline.copy_only_settings(); (*dst_spline_ptr).resize(total_points); @@ -581,8 +581,8 @@ static void calculate_curve_fillet(GeometrySet &geometry_set, CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - fn::FieldEvaluator field_evaluator{field_context, domain_size}; + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + fn::FieldEvaluator field_evaluator{field_context, domain_num}; field_evaluator.add(radius_field); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index e0348f27e51..d9cc8bcf023 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -1,12 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BLI_array.hh" -#include "BLI_index_mask_ops.hh" -#include "BLI_length_parameterize.hh" -#include "BLI_task.hh" -#include "BLI_timeit.hh" +#include "GEO_resample_curves.hh" -#include "BKE_attribute_math.hh" #include "BKE_curves.hh" #include "UI_interface.h" @@ -56,556 +51,6 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } -/** Returns the number of evaluated points in each curve. Used to deselect curves with none. */ -class EvaluatedCountFieldInput final : public GeometryFieldInput { - public: - EvaluatedCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Evaluated Point Count") - { - category_ = Category::Generated; - } - - GVArray get_varray_for_context(const GeometryComponent &component, - const AttributeDomain domain, - IndexMask UNUSED(mask)) const final - { - if (component.type() == GEO_COMPONENT_TYPE_CURVE && domain == ATTR_DOMAIN_CURVE && - !component.is_empty()) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const Curves &curves_id = *curve_component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - curves.ensure_evaluated_offsets(); - return VArray<int>::ForFunc(curves.curves_num(), [&](const int64_t index) -> int { - return curves.evaluated_points_for_curve(index).size(); - }); - } - return {}; - } - - uint64_t hash() const override - { - /* Some random constant hash. */ - return 234905872379865; - } - - bool is_equal_to(const fn::FieldNode &other) const override - { - return dynamic_cast<const EvaluatedCountFieldInput *>(&other) != nullptr; - } -}; - -/** - * Return true if the attribute should be copied/interpolated to the result curves. - * Don't output attributes that correspond to curve types that have no curves in the result. - */ -static bool interpolate_attribute_to_curves(const AttributeIDRef &attribute_id, - const std::array<int, CURVE_TYPES_NUM> &type_counts) -{ - if (!attribute_id.is_named()) { - return true; - } - if (ELEM(attribute_id.name(), - "handle_type_left", - "handle_type_right", - "handle_left", - "handle_right")) { - return type_counts[CURVE_TYPE_BEZIER] != 0; - } - if (ELEM(attribute_id.name(), "nurbs_weight")) { - return type_counts[CURVE_TYPE_NURBS] != 0; - } - return true; -} - -/** - * Return true if the attribute should be copied to poly curves. - */ -static bool interpolate_attribute_to_poly_curve(const AttributeIDRef &attribute_id) -{ - static const Set<StringRef> no_interpolation{{ - "handle_type_left", - "handle_type_right", - "handle_position_right", - "handle_position_left", - "nurbs_weight", - }}; - return !(attribute_id.is_named() && no_interpolation.contains(attribute_id.name())); -} - -/** - * Retrieve spans from source and result attributes. - */ -static void retrieve_attribute_spans(const Span<AttributeIDRef> ids, - const CurveComponent &src_component, - CurveComponent &dst_component, - Vector<GSpan> &src, - Vector<GMutableSpan> &dst, - Vector<OutputAttribute> &dst_attributes) -{ - for (const int i : ids.index_range()) { - GVArray src_attribute = src_component.attribute_try_get_for_read(ids[i], ATTR_DOMAIN_POINT); - BLI_assert(src_attribute); - src.append(src_attribute.get_internal_span()); - - const CustomDataType data_type = bke::cpp_type_to_custom_data_type(src_attribute.type()); - OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( - ids[i], ATTR_DOMAIN_POINT, data_type); - dst.append(dst_attribute.as_span()); - dst_attributes.append(std::move(dst_attribute)); - } -} - -struct AttributesForInterpolation : NonCopyable, NonMovable { - Vector<GSpan> src; - Vector<GMutableSpan> dst; - - Vector<OutputAttribute> dst_attributes; - - Vector<GSpan> src_no_interpolation; - Vector<GMutableSpan> dst_no_interpolation; -}; - -/** - * Gather a set of all generic attribute IDs to copy to the result curves. - */ -static void gather_point_attributes_to_interpolate(const CurveComponent &src_component, - CurveComponent &dst_component, - AttributesForInterpolation &result) -{ - const Curves &dst_curves_id = *dst_component.get_for_read(); - const bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id.geometry); - - VectorSet<AttributeIDRef> ids; - VectorSet<AttributeIDRef> ids_no_interpolation; - src_component.attribute_foreach( - [&](const AttributeIDRef &id, const AttributeMetaData meta_data) { - if (meta_data.domain != ATTR_DOMAIN_POINT) { - return true; - } - if (!interpolate_attribute_to_curves(id, dst_curves.curve_type_counts())) { - return true; - } - if (interpolate_attribute_to_poly_curve(id)) { - ids.add_new(id); - } - else { - ids_no_interpolation.add_new(id); - } - return true; - }); - - /* Position is handled differently since it has non-generic interpolation for Bezier - * curves and because the evaluated positions are cached for each evaluated point. */ - ids.remove_contained("position"); - - retrieve_attribute_spans( - ids, src_component, dst_component, result.src, result.dst, result.dst_attributes); - - /* Attributes that aren't interpolated like Bezier handles still have to be be copied - * to the result when there are any unselected curves of the corresponding type. */ - retrieve_attribute_spans(ids_no_interpolation, - src_component, - dst_component, - result.src_no_interpolation, - result.dst_no_interpolation, - result.dst_attributes); -} - -/** - * Copy the provided point attribute values between all curves in the #curve_ranges index - * ranges, assuming that all curves are the same size in #src_curves and #dst_curves. - */ -template<typename T> -static void copy_between_curves(const bke::CurvesGeometry &src_curves, - const bke::CurvesGeometry &dst_curves, - const Span<IndexRange> curve_ranges, - const Span<T> src, - const MutableSpan<T> dst) -{ - threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) { - for (const IndexRange range : curve_ranges.slice(range)) { - const IndexRange src_points = src_curves.points_for_curves(range); - const IndexRange dst_points = dst_curves.points_for_curves(range); - /* The arrays might be large, so a threaded copy might make sense here too. */ - dst.slice(dst_points).copy_from(src.slice(src_points)); - } - }); -} -static void copy_between_curves(const bke::CurvesGeometry &src_curves, - const bke::CurvesGeometry &dst_curves, - const Span<IndexRange> unselected_ranges, - const GSpan src, - const GMutableSpan dst) -{ - attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - copy_between_curves(src_curves, dst_curves, unselected_ranges, src.typed<T>(), dst.typed<T>()); - }); -} - -/** - * Copy the size of every curve in #curve_ranges to the corresponding index in #counts. - */ -static void fill_curve_counts(const bke::CurvesGeometry &src_curves, - const Span<IndexRange> curve_ranges, - MutableSpan<int> counts) -{ - threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange ranges_range) { - for (const IndexRange curves_range : curve_ranges.slice(ranges_range)) { - for (const int i : curves_range) { - counts[i] = src_curves.points_for_curve(i).size(); - } - } - }); -} - -/** - * Turn an array of sizes into the offset at each index including all previous sizes. - */ -static void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets) -{ - int total = 0; - for (const int i : counts_to_offsets.index_range().drop_back(1)) { - const int count = counts_to_offsets[i]; - BLI_assert(count > 0); - counts_to_offsets[i] = total; - total += count; - } - counts_to_offsets.last() = total; -} - -/** - * Create new curves where the selected curves have been resampled with a number of uniform-length - * samples defined by the count field. Interpolate attributes to the result, with an accuracy that - * depends on the curve's resolution parameter. - * - * \warning The values provided by the #count_field must be 1 or greater. - * \warning Curves with no evaluated points must not be selected. - */ -static Curves *resample_to_uniform_count(const CurveComponent &src_component, - const Field<bool> &selection_field, - const Field<int> &count_field) -{ - const Curves &src_curves_id = *src_component.get_for_read(); - const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); - - /* Create the new curves without any points and evaluate the final count directly - * into the offsets array, in order to be accumulated into offsets later. */ - Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); - /* Directly copy curve attributes, since they stay the same (except for curve types). */ - CustomData_copy(&src_curves.curve_data, - &dst_curves.curve_data, - CD_MASK_ALL, - CD_DUPLICATE, - src_curves.curves_num()); - MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); - - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; - fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; - evaluator.set_selection(selection_field); - evaluator.add_with_destination(count_field, dst_offsets); - evaluator.evaluate(); - const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); - const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( - src_curves.curves_range(), nullptr); - - /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */ - fill_curve_counts(src_curves, unselected_ranges, dst_offsets); - accumulate_counts_to_offsets(dst_offsets); - dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); - - /* All resampled curves are poly curves. */ - dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); - - VArray<bool> curves_cyclic = src_curves.cyclic(); - VArray<int8_t> curve_types = src_curves.curve_types(); - Span<float3> evaluated_positions = src_curves.evaluated_positions(); - MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); - - AttributesForInterpolation attributes; - gather_point_attributes_to_interpolate(src_component, dst_component, attributes); - - src_curves.ensure_evaluated_lengths(); - - /* Sampling arbitrary attributes works by first interpolating them to the curve's standard - * "evaluated points" and then interpolating that result with the uniform samples. This is - * potentially wasteful when down-sampling a curve to many fewer points. There are two possible - * solutions: only sample the necessary points for interpolation, or first sample curve - * parameter/segment indices and evaluate the curve directly. */ - Array<int> sample_indices(dst_curves.points_num()); - Array<float> sample_factors(dst_curves.points_num()); - - /* Use a "for each group of curves: for each attribute: for each curve" pattern to work on - * smaller sections of data that ideally fit into CPU cache better than simply one attribute at a - * time or one curve at a time. */ - threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { - const IndexMask sliced_selection = selection.slice(selection_range); - - Vector<std::byte> evaluated_buffer; - - /* Gather uniform samples based on the accumulated lengths of the original curve. */ - for (const int i_curve : sliced_selection) { - const bool cyclic = curves_cyclic[i_curve]; - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - length_parameterize::create_uniform_samples( - src_curves.evaluated_lengths_for_curve(i_curve, cyclic), - curves_cyclic[i_curve], - sample_indices.as_mutable_span().slice(dst_points), - sample_factors.as_mutable_span().slice(dst_points)); - } - - /* For every attribute, evaluate attributes from every curve in the range in the original - * curve's "evaluated points", then use linear interpolation to sample to the result. */ - for (const int i_attribute : attributes.dst.index_range()) { - attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { - using T = decltype(dummy); - Span<T> src = attributes.src[i_attribute].typed<T>(); - MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); - - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - - if (curve_types[i_curve] == CURVE_TYPE_POLY) { - length_parameterize::linear_interpolation(src.slice(src_points), - sample_indices.as_span().slice(dst_points), - sample_factors.as_span().slice(dst_points), - dst.slice(dst_points)); - } - else { - const int evaluated_size = src_curves.evaluated_points_for_curve(i_curve).size(); - evaluated_buffer.clear(); - evaluated_buffer.resize(sizeof(T) * evaluated_size); - MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>(); - src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated); - - length_parameterize::linear_interpolation(evaluated.as_span(), - sample_indices.as_span().slice(dst_points), - sample_factors.as_span().slice(dst_points), - dst.slice(dst_points)); - } - } - }); - } - - /* Interpolate the evaluated positions to the resampled curves. */ - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - length_parameterize::linear_interpolation(evaluated_positions.slice(src_points), - sample_indices.as_span().slice(dst_points), - sample_factors.as_span().slice(dst_points), - dst_positions.slice(dst_points)); - } - - /* Fill the default value for non-interpolating attributes that still must be copied. */ - for (GMutableSpan dst : attributes.dst_no_interpolation) { - for (const int i_curve : sliced_selection) { - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); - } - } - }); - - /* Any attribute data from unselected curve points can be directly copied. */ - for (const int i : attributes.src.index_range()) { - copy_between_curves( - src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); - } - for (const int i : attributes.src_no_interpolation.index_range()) { - copy_between_curves(src_curves, - dst_curves, - unselected_ranges, - attributes.src_no_interpolation[i], - attributes.dst_no_interpolation[i]); - } - - /* Copy positions for unselected curves. */ - Span<float3> src_positions = src_curves.positions(); - copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); - - for (OutputAttribute &attribute : attributes.dst_attributes) { - attribute.save(); - } - - return dst_curves_id; -} - -/** - * Evaluate each selected curve to its implicit evaluated points. - * - * \warning Curves with no evaluated points must not be selected. - */ -static Curves *resample_to_evaluated(const CurveComponent &src_component, - const Field<bool> &selection_field) -{ - const Curves &src_curves_id = *src_component.get_for_read(); - const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); - - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; - fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; - evaluator.set_selection(selection_field); - evaluator.evaluate(); - const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); - const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( - src_curves.curves_range(), nullptr); - - Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); - /* Directly copy curve attributes, since they stay the same (except for curve types). */ - CustomData_copy(&src_curves.curve_data, - &dst_curves.curve_data, - CD_MASK_ALL, - CD_DUPLICATE, - src_curves.curves_num()); - /* All resampled curves are poly curves. */ - dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); - MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); - - src_curves.ensure_evaluated_offsets(); - threading::parallel_for(selection.index_range(), 4096, [&](IndexRange range) { - for (const int i : selection.slice(range)) { - dst_offsets[i] = src_curves.evaluated_points_for_curve(i).size(); - } - }); - fill_curve_counts(src_curves, unselected_ranges, dst_offsets); - accumulate_counts_to_offsets(dst_offsets); - - dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); - - /* Create the correct number of uniform-length samples for every selected curve. */ - Span<float3> evaluated_positions = src_curves.evaluated_positions(); - MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); - - AttributesForInterpolation attributes; - gather_point_attributes_to_interpolate(src_component, dst_component, attributes); - - threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { - const IndexMask sliced_selection = selection.slice(selection_range); - - /* Evaluate generic point attributes directly to the result attributes. */ - for (const int i_attribute : attributes.dst.index_range()) { - attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { - using T = decltype(dummy); - Span<T> src = attributes.src[i_attribute].typed<T>(); - MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); - - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - src_curves.interpolate_to_evaluated( - i_curve, src.slice(src_points), dst.slice(dst_points)); - } - }); - } - - /* Copy the evaluated positions to the selected curves. */ - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - dst_positions.slice(dst_points).copy_from(evaluated_positions.slice(src_points)); - } - - /* Fill the default value for non-interpolating attributes that still must be copied. */ - for (GMutableSpan dst : attributes.dst_no_interpolation) { - for (const int i_curve : sliced_selection) { - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); - } - } - }); - - /* Any attribute data from unselected curve points can be directly copied. */ - for (const int i : attributes.src.index_range()) { - copy_between_curves( - src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); - } - for (const int i : attributes.src_no_interpolation.index_range()) { - copy_between_curves(src_curves, - dst_curves, - unselected_ranges, - attributes.src_no_interpolation[i], - attributes.dst_no_interpolation[i]); - } - - /* Copy positions for unselected curves. */ - Span<float3> src_positions = src_curves.positions(); - copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); - - for (OutputAttribute &attribute : attributes.dst_attributes) { - attribute.save(); - } - - return dst_curves_id; -} - -/** - * Create a resampled curve point count field for both "uniform" options. - * The complexity is handled here in order to make the actual resampling functions simpler. - */ -static Field<int> get_curve_count_field(GeoNodeExecParams params, - const GeometryNodeCurveResampleMode mode) -{ - if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { - static fn::CustomMF_SI_SO<int, int> max_one_fn( - "Clamp Above One", - [](int value) { return std::max(1, value); }, - fn::CustomMF_presets::AllSpanOrSingle()); - auto clamp_op = std::make_shared<FieldOperation>( - FieldOperation(max_one_fn, {Field<int>(params.extract_input<Field<int>>("Count"))})); - - return Field<int>(std::move(clamp_op)); - } - - if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { - static fn::CustomMF_SI_SI_SO<float, float, int> get_count_fn( - "Length Input to Count", - [](const float curve_length, const float sample_length) { - /* Find the number of sampled segments by dividing the total length by - * the sample length. Then there is one more sampled point than segment. */ - const int count = int(curve_length / sample_length) + 1; - return std::max(1, count); - }, - fn::CustomMF_presets::AllSpanOrSingle()); - - auto get_count_op = std::make_shared<FieldOperation>( - FieldOperation(get_count_fn, - {Field<float>(std::make_shared<SplineLengthFieldInput>()), - params.extract_input<Field<float>>("Length")})); - - return Field<int>(std::move(get_count_op)); - } - - BLI_assert_unreachable(); - return {}; -} - -/** - * Create a selection field that removes curves without any evaluated points (invalid NURBS curves) - * from the original selection provided to the node. This is here to simplify the sampling actual - * resampling code. - */ -static Field<bool> get_selection_field(GeoNodeExecParams params) -{ - static fn::CustomMF_SI_SI_SO<bool, int, bool> get_selection_fn( - "Create Curve Selection", - [](const bool orig_selection, const int evaluated_points_num) { - return orig_selection && evaluated_points_num > 1; - }, - fn::CustomMF_presets::AllSpanOrSingle()); - - auto selection_op = std::make_shared<FieldOperation>( - FieldOperation(get_selection_fn, - {params.extract_input<Field<bool>>("Selection"), - Field<int>(std::make_shared<EvaluatedCountFieldInput>())})); - - return Field<bool>(std::move(selection_op)); -} - static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); @@ -613,35 +58,38 @@ static void node_geo_exec(GeoNodeExecParams params) const NodeGeometryCurveResample &storage = node_storage(params.node()); const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode; - const Field<bool> selection = get_selection_field(params); + const Field<bool> selection = params.extract_input<Field<bool>>("Selection"); switch (mode) { - case GEO_NODE_CURVE_RESAMPLE_COUNT: + case GEO_NODE_CURVE_RESAMPLE_COUNT: { + Field<int> count = params.extract_input<Field<int>>("Count"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { + if (!component->is_empty()) { + geometry.replace_curves(geometry::resample_to_count(*component, selection, count)); + } + } + }); + break; + } case GEO_NODE_CURVE_RESAMPLE_LENGTH: { - Field<int> count = get_curve_count_field(params, mode); - - geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curves()) { - return; + Field<int> length = params.extract_input<Field<float>>("Length"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { + if (!component->is_empty()) { + geometry.replace_curves(geometry::resample_to_length(*component, selection, length)); + } } - - Curves *result = resample_to_uniform_count( - *geometry_set.get_component_for_read<CurveComponent>(), selection, count); - - geometry_set.replace_curves(result); }); break; } case GEO_NODE_CURVE_RESAMPLE_EVALUATED: - geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curves()) { - return; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { + if (!component->is_empty()) { + geometry.replace_curves(geometry::resample_to_evaluated(*component, selection)); + } } - - Curves *result = resample_to_evaluated( - *geometry_set.get_component_for_read<CurveComponent>(), selection); - - geometry_set.replace_curves(result); }); break; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index de29735bd2d..64a174df599 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -27,9 +27,9 @@ static void node_geo_exec(GeoNodeExecParams params) Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE); - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + fn::FieldEvaluator selection_evaluator{field_context, domain_num}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc index 5d97720a4f8..ae36248b573 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc @@ -75,7 +75,7 @@ static Array<float> curve_length_point_domain(const bke::CurvesGeometry &curves) case CURVE_TYPE_CATMULL_ROM: { const int resolution = resolutions[i_curve]; for (const int i : IndexRange(points.size()).drop_back(1)) { - lengths[i + 1] = evaluated_lengths[resolution * i - 1]; + lengths[i + 1] = evaluated_lengths[resolution * i]; } break; } @@ -144,9 +144,9 @@ static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry return {}; } -static VArray<float> construct_curve_length_varray(const bke::CurvesGeometry &curves, - const IndexMask UNUSED(mask), - const AttributeDomain domain) +static VArray<float> construct_curve_length_parameter_varray(const bke::CurvesGeometry &curves, + const IndexMask UNUSED(mask), + const AttributeDomain domain) { curves.ensure_evaluated_lengths(); @@ -217,9 +217,9 @@ class CurveParameterFieldInput final : public GeometryFieldInput { } }; -class CurveLengthFieldInput final : public GeometryFieldInput { +class CurveLengthParameterFieldInput final : public GeometryFieldInput { public: - CurveLengthFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Length node") + CurveLengthParameterFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Length node") { category_ = Category::Generated; } @@ -233,7 +233,7 @@ class CurveLengthFieldInput final : public GeometryFieldInput { if (curve_component.has_curves()) { const Curves &curves_id = *curve_component.get_for_read(); const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - return construct_curve_length_varray(curves, mask, domain); + return construct_curve_length_parameter_varray(curves, mask, domain); } } return {}; @@ -247,7 +247,7 @@ class CurveLengthFieldInput final : public GeometryFieldInput { bool is_equal_to(const fn::FieldNode &other) const override { - return dynamic_cast<const CurveLengthFieldInput *>(&other) != nullptr; + return dynamic_cast<const CurveLengthParameterFieldInput *>(&other) != nullptr; } }; @@ -288,7 +288,7 @@ class IndexOnSplineFieldInput final : public GeometryFieldInput { static void node_geo_exec(GeoNodeExecParams params) { Field<float> parameter_field{std::make_shared<CurveParameterFieldInput>()}; - Field<float> length_field{std::make_shared<CurveLengthFieldInput>()}; + Field<float> length_field{std::make_shared<CurveLengthParameterFieldInput>()}; Field<int> index_on_spline_field{std::make_shared<IndexOnSplineFieldInput>()}; params.set_output("Factor", std::move(parameter_field)); params.set_output("Length", std::move(length_field)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index 4118448b237..500804e41f0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -33,52 +33,49 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -template<class T> -static void scale_input_assign(const Span<T> input, +template<typename T> +static void scale_input_assign(const Span<T> src, const int scale, const int offset, - const MutableSpan<T> r_output) + MutableSpan<T> dst) { - for (const int i : IndexRange(r_output.size())) { - r_output[i] = input[i * scale + offset]; + for (const int i : dst.index_range()) { + dst[i] = src[i * scale + offset]; } } -template<class T> -static void scale_output_assign(const Span<T> input, +template<typename T> +static void scale_output_assign(const Span<T> src, const int scale, const int offset, - const MutableSpan<T> &r_output) + MutableSpan<T> dst) { - for (const int i : IndexRange(input.size())) { - r_output[i * scale + offset] = input[i]; + for (const int i : src.index_range()) { + dst[i * scale + offset] = src[i]; } } -template<class T> -static void nurbs_to_bezier_assign(const Span<T> input, - const MutableSpan<T> r_output, - const KnotsMode knotsMode) +template<typename T> +static void nurbs_to_bezier_assign(const Span<T> src, + const MutableSpan<T> dst, + const KnotsMode knots_mode) { - const int input_size = input.size(); - const int output_size = r_output.size(); - - switch (knotsMode) { + switch (knots_mode) { case NURBS_KNOT_MODE_BEZIER: - scale_input_assign<T>(input, 3, 1, r_output); + scale_input_assign<T>(src, 3, 1, dst); break; case NURBS_KNOT_MODE_NORMAL: - for (const int i : IndexRange(output_size)) { - r_output[i] = input[(i + 1) % input_size]; + for (const int i : dst.index_range()) { + dst[i] = src[(i + 1) % src.size()]; } break; case NURBS_KNOT_MODE_ENDPOINT_BEZIER: case NURBS_KNOT_MODE_ENDPOINT: - for (const int i : IndexRange(1, output_size - 2)) { - r_output[i] = input[i + 1]; + for (const int i : dst.index_range().drop_back(1).drop_front(1)) { + dst[i] = src[i + 1]; } - r_output.first() = input.first(); - r_output.last() = input.last(); + dst.first() = src.first(); + dst.last() = src.last(); break; } } @@ -110,20 +107,20 @@ static void copy_attributes(const Spline &input_spline, Spline &output_spline, C static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_positions, const KnotsMode knots_mode) { - const int nurbs_positions_size = nurbs_positions.size(); + const int nurbs_positions_num = nurbs_positions.size(); Vector<float3> handle_positions; if (knots_mode == NURBS_KNOT_MODE_BEZIER) { - for (const int i : IndexRange(nurbs_positions_size)) { + for (const int i : IndexRange(nurbs_positions_num)) { if (i % 3 == 1) { continue; } handle_positions.append(nurbs_positions[i]); } - if (nurbs_positions_size % 3 == 1) { + if (nurbs_positions_num % 3 == 1) { handle_positions.pop_last(); } - else if (nurbs_positions_size % 3 == 2) { - const int last_index = nurbs_positions_size - 1; + else if (nurbs_positions_num % 3 == 2) { + const int last_index = nurbs_positions_num - 1; handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]); } } @@ -137,11 +134,11 @@ static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_po handle_positions.append(2 * nurbs_positions[0] - nurbs_positions[1]); handle_positions.append(nurbs_positions[1]); } - const int segments_size = nurbs_positions_size - 1; - const bool ignore_interior_segment = segments_size == 3 && is_periodic == false; + const int segments_num = nurbs_positions_num - 1; + const bool ignore_interior_segment = segments_num == 3 && is_periodic == false; if (ignore_interior_segment == false) { - const float mid_offset = (float)(segments_size - 1) / 2.0f; - for (const int i : IndexRange(1, segments_size - 2)) { + const float mid_offset = (float)(segments_num - 1) / 2.0f; + for (const int i : IndexRange(1, segments_num - 2)) { const int divisor = is_periodic ? 3 : std::min(3, (int)(-std::abs(i - mid_offset) + mid_offset + 1.0f)); @@ -154,7 +151,7 @@ static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_po } } } - const int last_index = nurbs_positions_size - 1; + const int last_index = nurbs_positions_num - 1; if (is_periodic) { handle_positions.append( nurbs_positions[last_index - 1] + @@ -357,7 +354,7 @@ static SplinePtr convert_to_nurbs(const Spline &input) static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryCurveSplineType &storage = node_storage(params.node()); - const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage.spline_type; + const GeometryNodeSplineType dst_type = (const GeometryNodeSplineType)storage.spline_type; GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); @@ -371,11 +368,11 @@ static void node_geo_exec(GeoNodeExecParams params) const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( *curve_component->get_for_read()); GeometryComponentFieldContext field_context{*curve_component, ATTR_DOMAIN_CURVE}; - const int domain_size = curve_component->attribute_domain_size(ATTR_DOMAIN_CURVE); + const int domain_num = curve_component->attribute_domain_num(ATTR_DOMAIN_CURVE); Span<SplinePtr> src_splines = curve->splines(); - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + fn::FieldEvaluator selection_evaluator{field_context, domain_num}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0); @@ -386,7 +383,7 @@ static void node_geo_exec(GeoNodeExecParams params) threading::parallel_for(src_splines.index_range(), 512, [&](IndexRange range) { for (const int i : range) { if (selection[i]) { - switch (output_type) { + switch (dst_type) { case GEO_NODE_SPLINE_TYPE_POLY: new_curve->splines()[i] = convert_to_poly_spline(*src_splines[i]); break; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 371556c04f1..4d8745bf79e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -30,9 +30,9 @@ static Array<int> get_subdivided_offsets(const Spline &spline, const VArray<int> &cuts, const int spline_offset) { - Array<int> offsets(spline.segments_size() + 1); + Array<int> offsets(spline.segments_num() + 1); int offset = 0; - for (const int i : IndexRange(spline.segments_size())) { + for (const int i : IndexRange(spline.segments_num())) { offsets[i] = offset; offset = offset + std::max(cuts[spline_offset + i], 0) + 1; } @@ -46,8 +46,8 @@ static void subdivide_attribute(Span<T> src, const bool is_cyclic, MutableSpan<T> dst) { - const int src_size = src.size(); - threading::parallel_for(IndexRange(src_size - 1), 1024, [&](IndexRange range) { + const int src_num = src.size(); + threading::parallel_for(IndexRange(src_num - 1), 1024, [&](IndexRange range) { for (const int i : range) { const int cuts = offsets[i + 1] - offsets[i]; dst[offsets[i]] = src[i]; @@ -60,7 +60,7 @@ static void subdivide_attribute(Span<T> src, }); if (is_cyclic) { - const int i = src_size - 1; + const int i = src_num - 1; const int cuts = offsets[i + 1] - offsets[i]; dst[offsets[i]] = src.last(); const float factor_delta = cuts == 0 ? 1.0f : 1.0f / cuts; @@ -86,7 +86,7 @@ static void subdivide_attribute(Span<T> src, static void subdivide_bezier_segment(const BezierSpline &src, const int index, const int offset, - const int result_size, + const int result_num, Span<float3> src_positions, Span<float3> src_handles_left, Span<float3> src_handles_right, @@ -106,11 +106,11 @@ static void subdivide_bezier_segment(const BezierSpline &src, if (is_last_cyclic_segment) { dst_type_left.first() = BEZIER_HANDLE_VECTOR; } - dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_VECTOR); - dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_VECTOR); + dst_type_left.slice(offset + 1, result_num).fill(BEZIER_HANDLE_VECTOR); + dst_type_right.slice(offset, result_num).fill(BEZIER_HANDLE_VECTOR); - const float factor_delta = 1.0f / result_size; - for (const int cut : IndexRange(result_size)) { + const float factor_delta = 1.0f / result_num; + for (const int cut : IndexRange(result_num)) { const float factor = cut * factor_delta; dst_positions[offset + cut] = attribute_math::mix2( factor, src_positions[index], src_positions[next_index]); @@ -120,10 +120,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, if (is_last_cyclic_segment) { dst_type_left.first() = BEZIER_HANDLE_FREE; } - dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_FREE); - dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_FREE); + dst_type_left.slice(offset + 1, result_num).fill(BEZIER_HANDLE_FREE); + dst_type_right.slice(offset, result_num).fill(BEZIER_HANDLE_FREE); - const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; + const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_num; /* Create a Bezier segment to update iteratively for every subdivision * and references to the meaningful values for ease of use. */ @@ -138,8 +138,8 @@ static void subdivide_bezier_segment(const BezierSpline &src, handle_prev = src_handles_right[index]; handle_next = src_handles_left[next_index]; - for (const int cut : IndexRange(result_size - 1)) { - const float parameter = 1.0f / (result_size - cut); + for (const int cut : IndexRange(result_num - 1)) { + const float parameter = 1.0f / (result_num - cut); const BezierSpline::InsertResult insert = temp.calculate_segment_insertion(0, 1, parameter); /* Copy relevant temporary data to the result. */ @@ -154,7 +154,7 @@ static void subdivide_bezier_segment(const BezierSpline &src, } /* Copy the handles for the last segment from the temporary spline. */ - dst_handles_right[offset + result_size - 1] = handle_prev; + dst_handles_right[offset + result_num - 1] = handle_prev; dst_handles_left[i_segment_last] = handle_next; } } @@ -287,9 +287,9 @@ static SplinePtr subdivide_spline(const Spline &spline, * of cuts is a real span (especially considering the note below). Using the offset at each * point facilitates subdividing in parallel later. */ Array<int> offsets = get_subdivided_offsets(spline, cuts, spline_offset); - const int result_size = offsets.last() + int(!spline.is_cyclic()); + const int result_num = offsets.last() + int(!spline.is_cyclic()); SplinePtr new_spline = spline.copy_only_settings(); - new_spline->resize(result_size); + new_spline->resize(result_num); subdivide_builtin_attributes(spline, offsets, *new_spline); subdivide_dynamic_attributes(spline, offsets, *new_spline); return new_spline; @@ -334,9 +334,9 @@ static void node_geo_exec(GeoNodeExecParams params) const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(cuts_field); evaluator.evaluate(); const VArray<int> &cuts = evaluator.get_evaluated<int>(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 894580f2932..73b2c400e90 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -90,7 +90,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, int offset = 0; for (const int i : IndexRange(size)) { offsets[i] = offset; - if (splines[i]->evaluated_points_size() > 0) { + if (splines[i]->evaluated_points_num() > 0) { offset += count; } } @@ -104,7 +104,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, int offset = 0; for (const int i : IndexRange(size)) { offsets[i] = offset; - if (splines[i]->evaluated_points_size() > 0) { + if (splines[i]->evaluated_points_num() > 0) { offset += splines[i]->length() / resolution + 1; } } @@ -120,7 +120,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, } /** - * \note: Relies on the fact that all attributes on point clouds are stored contiguously. + * \note Relies on the fact that all attributes on point clouds are stored contiguously. */ static GMutableSpan ensure_point_attribute(PointCloudComponent &points, const AttributeIDRef &attribute_id, @@ -240,18 +240,18 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, for (const int i : range) { const Spline &spline = *splines[i]; const int offset = offsets[i]; - const int size = offsets[i + 1] - offsets[i]; - if (size == 0) { + const int num = offsets[i + 1] - offsets[i]; + if (num == 0) { continue; } - const Array<float> uniform_samples = spline.sample_uniform_index_factors(size); + const Array<float> uniform_samples = spline.sample_uniform_index_factors(num); spline.sample_with_index_factors<float3>( - spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, size)); + spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, num)); spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()), uniform_samples, - data.radii.slice(offset, size)); + data.radii.slice(offset, num)); for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) { const AttributeIDRef attribute_id = item.key; @@ -260,14 +260,13 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, BLI_assert(spline.attributes.get_for_read(attribute_id)); GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline_span), - uniform_samples, - dst.slice(offset, size)); + spline.sample_with_index_factors( + spline.interpolate_to_evaluated(spline_span), uniform_samples, dst.slice(offset, num)); } if (!data.tangents.is_empty()) { Span<float3> src_tangents = spline.evaluated_tangents(); - MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, size); + MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, num); spline.sample_with_index_factors<float3>(src_tangents, uniform_samples, sampled_tangents); for (float3 &vector : sampled_tangents) { vector = math::normalize(vector); @@ -276,7 +275,7 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, if (!data.normals.is_empty()) { Span<float3> src_normals = spline.evaluated_normals(); - MutableSpan<float3> sampled_normals = data.normals.slice(offset, size); + MutableSpan<float3> sampled_normals = data.normals.slice(offset, num); spline.sample_with_index_factors<float3>(src_normals, uniform_samples, sampled_normals); for (float3 &vector : sampled_normals) { vector = math::normalize(vector); @@ -298,8 +297,8 @@ static void copy_spline_domain_attributes(const CurveEval &curve, for (const int i : curve.splines().index_range()) { const int offset = offsets[i]; - const int size = offsets[i + 1] - offsets[i]; - type.fill_assign_n(curve_attribute[i], dst[offset], size); + const int num = offsets[i + 1] - offsets[i]; + type.fill_assign_n(curve_attribute[i], dst[offset], num); } return true; @@ -329,13 +328,13 @@ static void node_geo_exec(GeoNodeExecParams params) curve->assert_valid_point_attributes(); const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines); - const int total_size = offsets.last(); - if (total_size == 0) { + const int total_num = offsets.last(); + if (total_num == 0) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_size)); + geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_num)); PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>(); ResultAttributes point_attributes = create_attributes_for_transfer( points, *curve, attribute_outputs); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index df360818313..c993a3d305d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -117,10 +117,10 @@ struct TrimLocation { }; template<typename T> -static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int size) +static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int num) { - BLI_assert(start_index + size - 1 <= data.size()); - memmove(data.data(), &data[start_index], sizeof(T) * size); + BLI_assert(start_index + num - 1 <= data.size()); + memmove(data.data(), &data[start_index], sizeof(T) * num); } /* Shift slice to start of span and modifies start and end data. */ @@ -129,17 +129,17 @@ static void linear_trim_data(const TrimLocation &start, const TrimLocation &end, MutableSpan<T> data) { - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; if (start.left_index > 0) { - shift_slice_to_start<T>(data, start.left_index, size); + shift_slice_to_start<T>(data, start.left_index, num); } const T start_data = mix2<T>(start.factor, data.first(), data[1]); - const T end_data = mix2<T>(end.factor, data[size - 2], data[size - 1]); + const T end_data = mix2<T>(end.factor, data[num - 2], data[num - 1]); data.first() = start_data; - data[size - 1] = end_data; + data[num - 1] = end_data; } /** @@ -152,12 +152,12 @@ static void linear_trim_to_output_data(const TrimLocation &start, Span<T> src, MutableSpan<T> dst) { - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; const T start_data = mix2<T>(start.factor, src[start.left_index], src[start.right_index]); const T end_data = mix2<T>(end.factor, src[end.left_index], src[end.right_index]); - dst.copy_from(src.slice(start.left_index, size)); + dst.copy_from(src.slice(start.left_index, num)); dst.first() = start_data; dst.last() = end_data; } @@ -175,8 +175,8 @@ static TrimLocation lookup_control_point_position(const Spline::LookupResult &lo const int right = left == (spline.size() - 1) ? 0 : left + 1; const float offset_in_segment = lookup.evaluated_index + lookup.factor - offsets[left]; - const int segment_eval_size = offsets[left + 1] - offsets[left]; - const float factor = std::clamp(offset_in_segment / segment_eval_size, 0.0f, 1.0f); + const int segment_eval_num = offsets[left + 1] - offsets[left]; + const float factor = std::clamp(offset_in_segment / segment_eval_num, 0.0f, 1.0f); return {left, right, factor}; } @@ -191,7 +191,7 @@ static void trim_poly_spline(Spline &spline, const TrimLocation end = { end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor}; - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; linear_trim_data<float3>(start, end, spline.positions()); linear_trim_data<float>(start, end, spline.radii()); @@ -209,7 +209,7 @@ static void trim_poly_spline(Spline &spline, }, ATTR_DOMAIN_POINT); - spline.resize(size); + spline.resize(num); } /** @@ -225,11 +225,11 @@ static PolySpline trim_nurbs_spline(const Spline &spline, const TrimLocation end = { end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor}; - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; /* Create poly spline and copy trimmed data to it. */ PolySpline new_spline; - new_spline.resize(size); + new_spline.resize(num); /* Copy generic attribute data. */ spline.attributes.foreach_attribute( @@ -283,7 +283,7 @@ static void trim_bezier_spline(Spline &spline, const Span<int> control_offsets = bezier_spline.control_point_offsets(); /* The number of control points in the resulting spline. */ - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; /* Trim the spline attributes. Done before end.factor recalculation as it needs * the original end.factor value. */ @@ -301,10 +301,10 @@ static void trim_bezier_spline(Spline &spline, }, ATTR_DOMAIN_POINT); - /* Recalculate end.factor if the size is two, because the adjustment in the + /* Recalculate end.factor if the `num` is two, because the adjustment in the * position of the control point of the spline to the left of the new end point will change the * factor between them. */ - if (size == 2) { + if (num == 2) { if (start_lookup.factor == 1.0f) { end.factor = 0.0f; } @@ -328,38 +328,38 @@ static void trim_bezier_spline(Spline &spline, const BezierSpline::InsertResult end_point = bezier_spline.calculate_segment_insertion( end.left_index, end.right_index, end.factor); - /* If size is two, then the start point right handle needs to change to reflect the end point + /* If `num` is two, then the start point right handle needs to change to reflect the end point * previous handle update. */ - if (size == 2) { + if (num == 2) { start_point.right_handle = end_point.handle_prev; } /* Shift control point position data to start at beginning of array. */ if (start.left_index > 0) { - shift_slice_to_start(bezier_spline.positions(), start.left_index, size); - shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, size); - shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, size); + shift_slice_to_start(bezier_spline.positions(), start.left_index, num); + shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, num); + shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, num); } bezier_spline.positions().first() = start_point.position; - bezier_spline.positions()[size - 1] = end_point.position; + bezier_spline.positions()[num - 1] = end_point.position; bezier_spline.handle_positions_left().first() = start_point.left_handle; - bezier_spline.handle_positions_left()[size - 1] = end_point.left_handle; + bezier_spline.handle_positions_left()[num - 1] = end_point.left_handle; bezier_spline.handle_positions_right().first() = start_point.right_handle; - bezier_spline.handle_positions_right()[size - 1] = end_point.right_handle; + bezier_spline.handle_positions_right()[num - 1] = end_point.right_handle; /* If there is at least one control point between the endpoints, update the control * point handle to the right of the start point and to the left of the end point. */ - if (size > 2) { + if (num > 2) { bezier_spline.handle_positions_left()[start.right_index - start.left_index] = start_point.handle_next; bezier_spline.handle_positions_right()[end.left_index - start.left_index] = end_point.handle_prev; } - bezier_spline.resize(size); + bezier_spline.resize(num); } static void trim_spline(SplinePtr &spline, @@ -506,9 +506,9 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(start_field); evaluator.add(end_field); evaluator.evaluate(); @@ -527,7 +527,7 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, continue; } - if (spline->evaluated_edges_size() == 0) { + if (spline->evaluated_edges_num() == 0) { continue; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 8a0c900fbde..99edc4d298c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -470,7 +470,7 @@ static void separate_curve_selection(GeometrySet &geometry_set, GeometryComponentFieldContext field_context{src_component, selection_domain}; fn::FieldEvaluator selection_evaluator{field_context, - src_component.attribute_domain_size(selection_domain)}; + src_component.attribute_domain_num(selection_domain)}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); @@ -493,7 +493,7 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set, GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; fn::FieldEvaluator selection_evaluator{field_context, - src_points.attribute_domain_size(ATTR_DOMAIN_POINT)}; + src_points.attribute_domain_num(ATTR_DOMAIN_POINT)}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); @@ -526,8 +526,8 @@ static void separate_instance_selection(GeometrySet &geometry_set, InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; - const int domain_size = instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + const int domain_num = instances.attribute_domain_num(ATTR_DOMAIN_INSTANCE); + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(selection_field); evaluator.evaluate(); const VArray_Span<bool> &selection = evaluator.get_evaluated<bool>(0); @@ -1238,7 +1238,7 @@ static void separate_mesh_selection(GeometrySet &geometry_set, GeometryComponentFieldContext field_context{src_component, selection_domain}; fn::FieldEvaluator selection_evaluator{field_context, - src_component.attribute_domain_size(selection_domain)}; + src_component.attribute_domain_num(selection_domain)}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index d9f29d1ef1c..c242cfd5cf3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -398,11 +398,11 @@ static Array<float> calc_full_density_factors_with_selection(const MeshComponent { const AttributeDomain attribute_domain = ATTR_DOMAIN_CORNER; GeometryComponentFieldContext field_context{component, attribute_domain}; - const int domain_size = component.attribute_domain_size(attribute_domain); + const int domain_num = component.attribute_domain_num(attribute_domain); - Array<float> densities(domain_size, 0.0f); + Array<float> densities(domain_num, 0.0f); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(density_field, densities.as_mutable_span()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index 1b26cfe31fe..90837486186 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -351,19 +351,19 @@ static void duplicate_curves(GeometrySet &geometry_set, Array<int> curve_offsets(selection.size() + 1); Array<int> point_offsets(selection.size() + 1); - int dst_curves_size = 0; - int dst_points_size = 0; + int dst_curves_num = 0; + int dst_points_num = 0; for (const int i_curve : selection.index_range()) { const int count = std::max(counts[selection[i_curve]], 0); - curve_offsets[i_curve] = dst_curves_size; - point_offsets[i_curve] = dst_points_size; - dst_curves_size += count; - dst_points_size += count * curves.points_for_curve(selection[i_curve]).size(); + curve_offsets[i_curve] = dst_curves_num; + point_offsets[i_curve] = dst_points_num; + dst_curves_num += count; + dst_points_num += count * curves.points_for_curve(selection[i_curve]).size(); } - curve_offsets.last() = dst_curves_size; - point_offsets.last() = dst_points_size; + curve_offsets.last() = dst_curves_num; + point_offsets.last() = dst_points_num; - Curves *new_curves_id = bke::curves_new_nomain(dst_points_size, dst_curves_size); + Curves *new_curves_id = bke::curves_new_nomain(dst_points_num, dst_curves_num); bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry); MutableSpan<int> all_dst_offsets = new_curves.offsets_for_write(); @@ -379,7 +379,7 @@ static void duplicate_curves(GeometrySet &geometry_set, } } }); - all_dst_offsets.last() = dst_points_size; + all_dst_offsets.last() = dst_points_num; CurveComponent dst_component; dst_component.replace(new_curves_id, GeometryOwnershipType::Editable); @@ -807,23 +807,23 @@ static void duplicate_points_curve(GeometrySet &geometry_set, const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); Array<int> offsets = accumulate_counts_to_offsets(selection, counts); - const int dst_size = offsets.last(); + const int dst_num = offsets.last(); Array<int> point_to_curve_map(src_curves.points_num()); threading::parallel_for(src_curves.curves_range(), 1024, [&](const IndexRange range) { for (const int i_curve : range) { - const IndexRange point_range = src_curves.points_for_curve(i_curve); - point_to_curve_map.as_mutable_span().slice(point_range).fill(i_curve); + const IndexRange points = src_curves.points_for_curve(i_curve); + point_to_curve_map.as_mutable_span().slice(points).fill(i_curve); } }); - Curves *new_curves_id = bke::curves_new_nomain(dst_size, dst_size); + Curves *new_curves_id = bke::curves_new_nomain(dst_num, dst_num); bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry); MutableSpan<int> new_curve_offsets = new_curves.offsets_for_write(); for (const int i : new_curves.curves_range()) { new_curve_offsets[i] = i; } - new_curve_offsets.last() = dst_size; + new_curve_offsets.last() = dst_num; CurveComponent dst_component; dst_component.replace(new_curves_id, GeometryOwnershipType::Editable); @@ -940,10 +940,10 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set, { const PointCloudComponent &src_points = *geometry_set.get_component_for_read<PointCloudComponent>(); - const int point_size = src_points.attribute_domain_size(ATTR_DOMAIN_POINT); + const int point_num = src_points.attribute_domain_num(ATTR_DOMAIN_POINT); GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{field_context, point_size}; + FieldEvaluator evaluator{field_context, point_num}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); @@ -1026,7 +1026,7 @@ static void duplicate_instances(GeometrySet &geometry_set, *geometry_set.get_component_for_read<InstancesComponent>(); GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE}; - FieldEvaluator evaluator{field_context, src_instances.instances_amount()}; + FieldEvaluator evaluator{field_context, src_instances.instances_num()}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index 84acab47661..94fbec66bfe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -57,8 +57,8 @@ static void node_geo_exec(GeoNodeExecParams params) const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>(); GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE}; - const int domain_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + const int domain_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE); + fn::FieldEvaluator selection_evaluator{field_context, domain_num}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index 4591cfa1da2..1f16e8d55bd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -109,7 +109,7 @@ static MutableSpan<MLoop> mesh_loops(Mesh &mesh) } /** - * \note: Some areas in this file rely on the new sections of attributes from #CustomData_realloc + * \note Some areas in this file rely on the new sections of attributes from #CustomData_realloc * to be zeroed. */ static void expand_mesh(Mesh &mesh, diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc index 77be98f169e..bf956f3b83d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc @@ -91,7 +91,7 @@ class FieldAtIndex final : public GeometryFieldInput { { const GeometryComponentFieldContext value_field_context{component, value_field_domain_}; FieldEvaluator value_evaluator{value_field_context, - component.attribute_domain_size(value_field_domain_)}; + component.attribute_domain_num(value_field_domain_)}; value_evaluator.add(value_field_); value_evaluator.evaluate(); const GVArray &values = value_evaluator.get_evaluated(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc index 34c3169065c..0484017cf3b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc @@ -22,11 +22,11 @@ static void node_declare(NodeDeclarationBuilder &b) static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selection_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE); + if (domain_num == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc index d39291a2a7e..8e3a9b6769d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc @@ -101,8 +101,7 @@ class IslandCountFieldInput final : public GeometryFieldInput { island_list.add(root); } - return VArray<int>::ForSingle(island_list.size(), - mesh_component.attribute_domain_size(domain)); + return VArray<int>::ForSingle(island_list.size(), mesh_component.attribute_domain_num(domain)); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc index 6c24f86b63b..84d773ff8eb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc @@ -4,71 +4,6 @@ #include "BKE_curves.hh" -namespace blender::nodes { - -/* -------------------------------------------------------------------- - * Spline Length - */ - -static VArray<float> construct_spline_length_gvarray(const CurveComponent &component, - const AttributeDomain domain) -{ - if (!component.has_curves()) { - return {}; - } - const Curves &curves_id = *component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - - curves.ensure_evaluated_lengths(); - - VArray<bool> cyclic = curves.cyclic(); - VArray<float> lengths = VArray<float>::ForFunc( - curves.curves_num(), [&curves, cyclic = std::move(cyclic)](int64_t index) { - return curves.evaluated_length_total_for_curve(index, cyclic[index]); - }); - - if (domain == ATTR_DOMAIN_CURVE) { - return lengths; - } - - if (domain == ATTR_DOMAIN_POINT) { - return component.attribute_try_adapt_domain<float>( - std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); - } - - return {}; -} - -SplineLengthFieldInput::SplineLengthFieldInput() - : GeometryFieldInput(CPPType::get<float>(), "Spline Length node") -{ - category_ = Category::Generated; -} - -GVArray SplineLengthFieldInput::get_varray_for_context(const GeometryComponent &component, - const AttributeDomain domain, - IndexMask UNUSED(mask)) const -{ - if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - return construct_spline_length_gvarray(curve_component, domain); - } - return {}; -} - -uint64_t SplineLengthFieldInput::hash() const -{ - /* Some random constant hash. */ - return 3549623580; -} - -bool SplineLengthFieldInput::is_equal_to(const fn::FieldNode &other) const -{ - return dynamic_cast<const SplineLengthFieldInput *>(&other) != nullptr; -} - -} // namespace blender::nodes - namespace blender::nodes::node_geo_input_spline_length_cc { static void node_declare(NodeDeclarationBuilder &b) @@ -81,8 +16,8 @@ static void node_declare(NodeDeclarationBuilder &b) * Spline Count */ -static VArray<int> construct_spline_count_gvarray(const CurveComponent &component, - const AttributeDomain domain) +static VArray<int> construct_curve_point_count_gvarray(const CurveComponent &component, + const AttributeDomain domain) { if (!component.has_curves()) { return {}; @@ -117,7 +52,7 @@ class SplineCountFieldInput final : public GeometryFieldInput { { if (component.type() == GEO_COMPONENT_TYPE_CURVE) { const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - return construct_spline_count_gvarray(curve_component, domain); + return construct_curve_point_count_gvarray(curve_component, domain); } return {}; } @@ -136,7 +71,7 @@ class SplineCountFieldInput final : public GeometryFieldInput { static void node_geo_exec(GeoNodeExecParams params) { - Field<float> spline_length_field{std::make_shared<SplineLengthFieldInput>()}; + Field<float> spline_length_field{std::make_shared<bke::CurveLengthFieldInput>()}; Field<int> spline_count_field{std::make_shared<SplineCountFieldInput>()}; params.set_output("Length", std::move(spline_length_field)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 61f719ade4e..12582f9e9c6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -50,7 +50,7 @@ static void add_instances_from_component( const Map<AttributeIDRef, AttributeKind> &attributes_to_propagate) { const AttributeDomain domain = ATTR_DOMAIN_POINT; - const int domain_size = src_component.attribute_domain_size(domain); + const int domain_num = src_component.attribute_domain_num(domain); VArray<bool> pick_instance; VArray<int> indices; @@ -59,7 +59,7 @@ static void add_instances_from_component( GeometryComponentFieldContext field_context{src_component, domain}; const Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); /* The evaluator could use the component's stable IDs as a destination directly, but only the * selected indices should be copied. */ @@ -73,7 +73,7 @@ static void add_instances_from_component( /* The initial size of the component might be non-zero when this function is called for multiple * component types. */ - const int start_len = dst_component.instances_amount(); + const int start_len = dst_component.instances_num(); const int select_len = selection.index_range().size(); dst_component.resize(start_len + select_len); @@ -119,12 +119,12 @@ static void add_instances_from_component( const bool use_individual_instance = pick_instance[i]; if (use_individual_instance) { if (src_instances != nullptr) { - const int src_instances_amount = src_instances->instances_amount(); + const int src_instances_num = src_instances->instances_num(); const int original_index = indices[i]; /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1 * refers to the last element. */ - const int index = mod_i(original_index, std::max(src_instances_amount, 1)); - if (index < src_instances_amount) { + const int index = mod_i(original_index, std::max(src_instances_num, 1)); + if (index < src_instances_num) { /* Get the reference to the source instance. */ const int src_handle = src_instances->instance_reference_handles()[index]; dst_handle = handle_mapping[src_handle]; diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index e35f152ce49..2126a5cc329 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -40,9 +40,9 @@ static void convert_instances_to_points(GeometrySet &geometry_set, const InstancesComponent &instances = *geometry_set.get_component_for_read<InstancesComponent>(); GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; - const int domain_size = instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE); + const int domain_num = instances.attribute_domain_num(ATTR_DOMAIN_INSTANCE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(std::move(selection_field)); evaluator.add(std::move(position_field)); evaluator.add(std::move(radius_field)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 0e2803cd035..2067086c298 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -56,8 +56,8 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components, int offset = 0; for (const GeometryComponent *component : src_components) { - const int domain_size = component->attribute_domain_size(domain); - if (domain_size == 0) { + const int domain_num = component->attribute_domain_num(domain); + if (domain_num == 0) { continue; } GVArray read_attribute = component->attribute_get_for_read( @@ -66,9 +66,9 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components, GVArray_GSpan src_span{read_attribute}; const void *src_buffer = src_span.data(); void *dst_buffer = dst_span[offset]; - cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_size); + cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_num); - offset += domain_size; + offset += domain_num; } } @@ -101,7 +101,7 @@ static void join_components(Span<const InstancesComponent *> src_components, Geo int tot_instances = 0; for (const InstancesComponent *src_component : src_components) { - tot_instances += src_component->instances_amount(); + tot_instances += src_component->instances_num(); } dst_component.reserve(tot_instances); diff --git a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc index 1ec97858d4d..1def4089115 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc @@ -39,9 +39,9 @@ static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_p const float merge_distance, const Field<bool> &selection_field) { - const int src_size = src_points.attribute_domain_size(ATTR_DOMAIN_POINT); + const int src_num = src_points.attribute_domain_num(ATTR_DOMAIN_POINT); GeometryComponentFieldContext context{src_points, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_size}; + FieldEvaluator evaluator{context, src_num}; evaluator.add(selection_field); evaluator.evaluate(); @@ -57,10 +57,10 @@ static std::optional<Mesh *> mesh_merge_by_distance_connected(const MeshComponen const float merge_distance, const Field<bool> &selection_field) { - const int src_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - Array<bool> selection(src_size); + const int src_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT); + Array<bool> selection(src_num); GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_size}; + FieldEvaluator evaluator{context, src_num}; evaluator.add_with_destination(selection_field, selection.as_mutable_span()); evaluator.evaluate(); @@ -72,9 +72,9 @@ static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mes const float merge_distance, const Field<bool> &selection_field) { - const int src_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); + const int src_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT); GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_size}; + FieldEvaluator evaluator{context, src_num}; evaluator.add(selection_field); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index 636ecb8ab41..0029b547375 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -6,414 +6,9 @@ #include "BKE_material.h" #include "BKE_mesh.h" -#include "node_geometry_util.hh" - -namespace blender::nodes { - -struct CuboidConfig { - float3 size; - int verts_x; - int verts_y; - int verts_z; - int edges_x; - int edges_y; - int edges_z; - int vertex_count; - int poly_count; - int loop_count; - - CuboidConfig(float3 size, int verts_x, int verts_y, int verts_z) - : size(size), - verts_x(verts_x), - verts_y(verts_y), - verts_z(verts_z), - edges_x(verts_x - 1), - edges_y(verts_y - 1), - edges_z(verts_z - 1) - { - BLI_assert(edges_x > 0 && edges_y > 0 && edges_z > 0); - this->vertex_count = this->get_vertex_count(); - this->poly_count = this->get_poly_count(); - this->loop_count = this->poly_count * 4; - } - - private: - int get_vertex_count() - { - const int inner_position_count = (verts_x - 2) * (verts_y - 2) * (verts_z - 2); - return verts_x * verts_y * verts_z - inner_position_count; - } - - int get_poly_count() - { - return 2 * (edges_x * edges_y + edges_y * edges_z + edges_z * edges_x); - } -}; - -static void calculate_vertices(const CuboidConfig &config, MutableSpan<MVert> verts) -{ - const float z_bottom = -config.size.z / 2.0f; - const float z_delta = config.size.z / config.edges_z; - - const float x_left = -config.size.x / 2.0f; - const float x_delta = config.size.x / config.edges_x; - - const float y_front = -config.size.y / 2.0f; - const float y_delta = config.size.y / config.edges_y; - - int vert_index = 0; - - for (const int z : IndexRange(config.verts_z)) { - if (ELEM(z, 0, config.edges_z)) { - /* Fill bottom and top. */ - const float z_pos = z_bottom + z_delta * z; - for (const int y : IndexRange(config.verts_y)) { - const float y_pos = y_front + y_delta * y; - for (const int x : IndexRange(config.verts_x)) { - const float x_pos = x_left + x_delta * x; - copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); - } - } - } - else { - for (const int y : IndexRange(config.verts_y)) { - if (ELEM(y, 0, config.edges_y)) { - /* Fill y-sides. */ - const float y_pos = y_front + y_delta * y; - const float z_pos = z_bottom + z_delta * z; - for (const int x : IndexRange(config.verts_x)) { - const float x_pos = x_left + x_delta * x; - copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); - } - } - else { - /* Fill x-sides. */ - const float x_pos = x_left; - const float y_pos = y_front + y_delta * y; - const float z_pos = z_bottom + z_delta * z; - copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); - const float x_pos2 = x_left + x_delta * config.edges_x; - copy_v3_v3(verts[vert_index++].co, float3(x_pos2, y_pos, z_pos)); - } - } - } - } -} - -/* vert_1 = bottom left, vert_2 = bottom right, vert_3 = top right, vert_4 = top left. - * Hence they are passed as 1,4,3,2 when calculating polys clockwise, and 1,2,3,4 for - * anti-clockwise. - */ -static void define_quad(MutableSpan<MPoly> polys, - MutableSpan<MLoop> loops, - const int poly_index, - const int loop_index, - const int vert_1, - const int vert_2, - const int vert_3, - const int vert_4) -{ - MPoly &poly = polys[poly_index]; - poly.loopstart = loop_index; - poly.totloop = 4; - - MLoop &loop_1 = loops[loop_index]; - loop_1.v = vert_1; - MLoop &loop_2 = loops[loop_index + 1]; - loop_2.v = vert_2; - MLoop &loop_3 = loops[loop_index + 2]; - loop_3.v = vert_3; - MLoop &loop_4 = loops[loop_index + 3]; - loop_4.v = vert_4; -} - -static void calculate_polys(const CuboidConfig &config, - MutableSpan<MPoly> polys, - MutableSpan<MLoop> loops) -{ - int loop_index = 0; - int poly_index = 0; - - /* Number of vertices in an XY cross-section of the cube (barring top and bottom faces). */ - const int xy_cross_section_vert_count = config.verts_x * config.verts_y - - (config.verts_x - 2) * (config.verts_y - 2); +#include "GEO_mesh_primitive_cuboid.hh" - /* Calculate polys for Bottom faces. */ - int vert_1_start = 0; - - for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { - for (const int x : IndexRange(config.edges_x)) { - const int vert_1 = vert_1_start + x; - const int vert_2 = vert_1_start + config.verts_x + x; - const int vert_3 = vert_2 + 1; - const int vert_4 = vert_1 + 1; - - define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); - loop_index += 4; - poly_index++; - } - vert_1_start += config.verts_x; - } - - /* Calculate polys for Front faces. */ - vert_1_start = 0; - int vert_2_start = config.verts_x * config.verts_y; - - for ([[maybe_unused]] const int z : IndexRange(config.edges_z)) { - for (const int x : IndexRange(config.edges_x)) { - define_quad(polys, - loops, - poly_index, - loop_index, - vert_1_start + x, - vert_1_start + x + 1, - vert_2_start + x + 1, - vert_2_start + x); - loop_index += 4; - poly_index++; - } - vert_1_start = vert_2_start; - vert_2_start += config.verts_x * config.verts_y - (config.verts_x - 2) * (config.verts_y - 2); - } - - /* Calculate polys for Top faces. */ - vert_1_start = config.verts_x * config.verts_y + - (config.verts_z - 2) * (config.verts_x * config.verts_y - - (config.verts_x - 2) * (config.verts_y - 2)); - vert_2_start = vert_1_start + config.verts_x; - - for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { - for (const int x : IndexRange(config.edges_x)) { - define_quad(polys, - loops, - poly_index, - loop_index, - vert_1_start + x, - vert_1_start + x + 1, - vert_2_start + x + 1, - vert_2_start + x); - loop_index += 4; - poly_index++; - } - vert_2_start += config.verts_x; - vert_1_start += config.verts_x; - } - - /* Calculate polys for Back faces. */ - vert_1_start = config.verts_x * config.edges_y; - vert_2_start = vert_1_start + xy_cross_section_vert_count; - - for (const int z : IndexRange(config.edges_z)) { - if (z == (config.edges_z - 1)) { - vert_2_start += (config.verts_x - 2) * (config.verts_y - 2); - } - for (const int x : IndexRange(config.edges_x)) { - define_quad(polys, - loops, - poly_index, - loop_index, - vert_1_start + x, - vert_2_start + x, - vert_2_start + x + 1, - vert_1_start + x + 1); - loop_index += 4; - poly_index++; - } - vert_2_start += xy_cross_section_vert_count; - vert_1_start += xy_cross_section_vert_count; - } - - /* Calculate polys for Left faces. */ - vert_1_start = 0; - vert_2_start = config.verts_x * config.verts_y; - - for (const int z : IndexRange(config.edges_z)) { - for (const int y : IndexRange(config.edges_y)) { - int vert_1; - int vert_2; - int vert_3; - int vert_4; - - if (z == 0 || y == 0) { - vert_1 = vert_1_start + config.verts_x * y; - vert_4 = vert_1 + config.verts_x; - } - else { - vert_1 = vert_1_start + 2 * y; - vert_1 += config.verts_x - 2; - vert_4 = vert_1 + 2; - } - - if (y == 0 || z == (config.edges_z - 1)) { - vert_2 = vert_2_start + config.verts_x * y; - vert_3 = vert_2 + config.verts_x; - } - else { - vert_2 = vert_2_start + 2 * y; - vert_2 += config.verts_x - 2; - vert_3 = vert_2 + 2; - } - - define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); - loop_index += 4; - poly_index++; - } - if (z == 0) { - vert_1_start += config.verts_x * config.verts_y; - } - else { - vert_1_start += xy_cross_section_vert_count; - } - vert_2_start += xy_cross_section_vert_count; - } - - /* Calculate polys for Right faces. */ - vert_1_start = config.edges_x; - vert_2_start = vert_1_start + config.verts_x * config.verts_y; - - for (const int z : IndexRange(config.edges_z)) { - for (const int y : IndexRange(config.edges_y)) { - int vert_1 = vert_1_start; - int vert_2 = vert_2_start; - int vert_3 = vert_2_start + 2; - int vert_4 = vert_1 + config.verts_x; - - if (z == 0) { - vert_1 = vert_1_start + config.verts_x * y; - vert_4 = vert_1 + config.verts_x; - } - else { - vert_1 = vert_1_start + 2 * y; - vert_4 = vert_1 + 2; - } - - if (z == (config.edges_z - 1)) { - vert_2 = vert_2_start + config.verts_x * y; - vert_3 = vert_2 + config.verts_x; - } - else { - vert_2 = vert_2_start + 2 * y; - vert_3 = vert_2 + 2; - } - - if (y == (config.edges_y - 1)) { - vert_3 = vert_2 + config.verts_x; - vert_4 = vert_1 + config.verts_x; - } - - define_quad(polys, loops, poly_index, loop_index, vert_1, vert_4, vert_3, vert_2); - loop_index += 4; - poly_index++; - } - if (z == 0) { - vert_1_start += config.verts_x * config.verts_y; - } - else { - vert_1_start += xy_cross_section_vert_count; - } - vert_2_start += xy_cross_section_vert_count; - } -} - -static void calculate_uvs(const CuboidConfig &config, Mesh *mesh) -{ - MeshComponent mesh_component; - mesh_component.replace(mesh, GeometryOwnershipType::Editable); - OutputAttribute_Typed<float2> uv_attribute = - mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER); - MutableSpan<float2> uvs = uv_attribute.as_span(); - - int loop_index = 0; - - const float x_delta = 0.25f / static_cast<float>(config.edges_x); - const float y_delta = 0.25f / static_cast<float>(config.edges_y); - const float z_delta = 0.25f / static_cast<float>(config.edges_z); - - /* Calculate bottom face UVs. */ - for (const int y : IndexRange(config.edges_y)) { - for (const int x : IndexRange(config.edges_x)) { - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - y * y_delta); - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - (y + 1) * y_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - (y + 1) * y_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - y * y_delta); - } - } - - /* Calculate front face UVs. */ - for (const int z : IndexRange(config.edges_z)) { - for (const int x : IndexRange(config.edges_x)) { - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + (z + 1) * z_delta); - } - } - - /* Calculate top face UVs. */ - for (const int y : IndexRange(config.edges_y)) { - for (const int x : IndexRange(config.edges_x)) { - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + y * y_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + y * y_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + (y + 1) * y_delta); - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + (y + 1) * y_delta); - } - } - - /* Calculate back face UVs. */ - for (const int z : IndexRange(config.edges_z)) { - for (const int x : IndexRange(config.edges_x)) { - uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + z * z_delta); - } - } - - /* Calculate left face UVs. */ - for (const int z : IndexRange(config.edges_z)) { - for (const int y : IndexRange(config.edges_y)) { - uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + z * z_delta); - } - } - - /* Calculate right face UVs. */ - for (const int z : IndexRange(config.edges_z)) { - for (const int y : IndexRange(config.edges_y)) { - uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + (z + 1) * z_delta); - } - } - - uv_attribute.save(); -} - -Mesh *create_cuboid_mesh(const float3 size, - const int verts_x, - const int verts_y, - const int verts_z) -{ - const CuboidConfig config(size, verts_x, verts_y, verts_z); - - Mesh *mesh = BKE_mesh_new_nomain( - config.vertex_count, 0, 0, config.loop_count, config.poly_count); - BKE_id_material_eval_ensure_default_slot(&mesh->id); - - calculate_vertices(config, {mesh->mvert, mesh->totvert}); - - calculate_polys(config, {mesh->mpoly, mesh->totpoly}, {mesh->mloop, mesh->totloop}); - BKE_mesh_calc_edges(mesh, false, false); - - calculate_uvs(config, mesh); - - return mesh; -} - -} // namespace blender::nodes +#include "node_geometry_util.hh" namespace blender::nodes::node_geo_mesh_primitive_cube_cc { @@ -442,6 +37,16 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Mesh")); } +static Mesh *create_cuboid_mesh(const float3 &size, + const int verts_x, + const int verts_y, + const int verts_z) +{ + Mesh *mesh = geometry::create_cuboid_mesh(size, verts_x, verts_y, verts_z, "uv_map"); + BKE_id_material_eval_ensure_default_slot(&mesh->id); + return mesh; +} + static Mesh *create_cube_mesh(const float3 size, const int verts_x, const int verts_y, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index f6ee3d00dee..ec6acf55dd8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -25,7 +25,7 @@ static void node_geo_exec(GeoNodeExecParams params) const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); GeometryComponentFieldContext context{component, ATTR_DOMAIN_EDGE}; - fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_EDGE)}; + fn::FieldEvaluator evaluator{context, component.attribute_domain_num(ATTR_DOMAIN_EDGE)}; evaluator.add(params.get_input<Field<bool>>("Selection")); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index 1a0cc53cb6c..6b23b685549 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_task.hh" + #include "DNA_pointcloud_types.h" #include "BKE_attribute_math.hh" @@ -41,14 +43,15 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -template<typename T> -static void copy_attribute_to_points(const VArray<T> &src, - const IndexMask mask, - MutableSpan<T> dst) +static void materialize_compressed_to_uninitialized_threaded(const GVArray &src, + const IndexMask mask, + GMutableSpan dst) { - for (const int i : mask.index_range()) { - dst[i] = src[mask[i]]; - } + BLI_assert(src.type() == dst.type()); + BLI_assert(mask.size() == dst.size()); + threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) { + src.materialize_compressed_to_uninitialized(mask.slice(range), dst.slice(range).data()); + }); } static void geometry_set_mesh_to_points(GeometrySet &geometry_set, @@ -63,12 +66,12 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, return; } GeometryComponentFieldContext field_context{*mesh_component, domain}; - const int domain_size = mesh_component->attribute_domain_size(domain); - if (domain_size == 0) { + const int domain_num = mesh_component->attribute_domain_num(domain); + if (domain_num == 0) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); /* Evaluating directly into the point cloud doesn't work because we are not using the full * "min_array_size" array but compressing the selected elements into the final array with no @@ -79,16 +82,21 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); - uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f); geometry_set.replace_pointcloud(pointcloud); PointCloudComponent &point_component = geometry_set.get_component_for_write<PointCloudComponent>(); - copy_attribute_to_points(evaluator.get_evaluated<float3>(0), - selection, - {(float3 *)pointcloud->co, pointcloud->totpoint}); - copy_attribute_to_points( - evaluator.get_evaluated<float>(1), selection, {pointcloud->radius, pointcloud->totpoint}); + OutputAttribute position = point_component.attribute_try_get_for_output_only( + "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + materialize_compressed_to_uninitialized_threaded( + evaluator.get_evaluated(0), selection, position.as_span()); + position.save(); + + OutputAttribute radius = point_component.attribute_try_get_for_output_only( + "radius", ATTR_DOMAIN_POINT, CD_PROP_FLOAT); + materialize_compressed_to_uninitialized_threaded( + evaluator.get_evaluated(1), selection, radius.as_span()); + radius.save(); Map<AttributeIDRef, AttributeKind> attributes; geometry_set.gather_attributes_for_propagation( @@ -102,11 +110,7 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, OutputAttribute dst = point_component.attribute_try_get_for_output_only( attribute_id, ATTR_DOMAIN_POINT, data_type); if (dst && src) { - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { - using T = decltype(dummy); - VArray<T> src_typed = src.typed<T>(); - copy_attribute_to_points(src_typed, selection, dst.as_span().typed<T>()); - }); + materialize_compressed_to_uninitialized_threaded(src, selection, dst.as_span()); dst.save(); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc index 0f6586617bc..577b001fd06 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -38,13 +38,13 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, } GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT}; - const int domain_size = point_component->attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = point_component->attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + fn::FieldEvaluator selection_evaluator{field_context, domain_num}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index c99b51ffd4c..42cee4c0efe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -168,14 +168,14 @@ static void gather_point_data_from_component(GeoNodeExecParams ¶ms, Field<float> radius_field = params.get_input<Field<float>>("Radius"); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); - r_positions.resize(r_positions.size() + domain_size); - positions.materialize(r_positions.as_mutable_span().take_back(domain_size)); + r_positions.resize(r_positions.size() + domain_num); + positions.materialize(r_positions.as_mutable_span().take_back(domain_num)); - r_radii.resize(r_radii.size() + domain_size); - fn::FieldEvaluator evaluator{field_context, domain_size}; - evaluator.add_with_destination(radius_field, r_radii.as_mutable_span().take_back(domain_size)); + r_radii.resize(r_radii.size() + domain_num); + fn::FieldEvaluator evaluator{field_context, domain_num}; + evaluator.add_with_destination(radius_field, r_radii.as_mutable_span().take_back(domain_num)); evaluator.evaluate(); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 368954447c9..0c30d50076f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -312,8 +312,8 @@ class RaycastFunction : public fn::MultiFunction { } const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>(); target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); - const int domain_size = mesh_component.attribute_domain_size(domain_); - target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size); + const int domain_num = mesh_component.attribute_domain_num(domain_); + target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_num); target_evaluator_->add(std::move(src_field)); target_evaluator_->evaluate(); target_data_ = &target_evaluator_->get_evaluated(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc index 6eb95859e50..59e203afd08 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc @@ -19,9 +19,9 @@ static void node_declare(NodeDeclarationBuilder &b) static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) { GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - const int domain_size = instances_component.instances_amount(); + const int domain_num = instances_component.instances_num(); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Rotation")); evaluator.add(params.extract_input<Field<float3>>("Pivot Point")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc index 4ca21874f8f..d4716a6b6f0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc @@ -23,7 +23,7 @@ static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &insta { GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - fn::FieldEvaluator evaluator{field_context, instances_component.instances_amount()}; + fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Scale")); evaluator.add(params.extract_input<Field<float3>>("Center")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index 73e49c7d037..d2082924fa7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -75,12 +75,12 @@ static void set_position_in_component(CurveComponent &component, const Field<float3> &offset_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add(position_field); evaluator.add(offset_field); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc index a23a6c09551..4c84093bfcb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -21,15 +21,15 @@ static void set_radius_in_component(GeometryComponent &component, const Field<float> &radius_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { return; } OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>( "radius", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(radius_field, radii.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc index 1155c97dc38..8b1e5935a61 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -17,15 +17,15 @@ static void set_tilt_in_component(GeometryComponent &component, const Field<float> &tilt_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { return; } OutputAttribute_Typed<float> tilts = component.attribute_try_get_for_output_only<float>( "tilt", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(tilt_field, tilts.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc index 0892e068ce2..ec95f9a89f5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc @@ -20,12 +20,12 @@ static void set_id_in_component(GeometryComponent &component, ATTR_DOMAIN_INSTANCE : ATTR_DOMAIN_POINT; GeometryComponentFieldContext field_context{component, domain}; - const int domain_size = component.attribute_domain_size(domain); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(domain); + if (domain_num == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); /* Since adding the ID attribute can change the result of the field evaluation (the random value diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc index a0b38209f97..58613dae832 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc @@ -17,15 +17,15 @@ static void set_material_index_in_component(GeometryComponent &component, const Field<int> &index_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE); + if (domain_num == 0) { return; } OutputAttribute_Typed<int> indices = component.attribute_try_get_for_output_only<int>( "material_index", ATTR_DOMAIN_FACE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(index_field, indices.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc index 93024fd81d6..571bead9743 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc @@ -21,15 +21,15 @@ static void set_radius_in_component(GeometryComponent &component, const Field<float> &radius_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { return; } OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>( "radius", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(radius_field, radii.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index d2ff9753897..caf33108716 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -143,12 +143,12 @@ static void set_position_in_component(GeometryComponent &component, ATTR_DOMAIN_INSTANCE : ATTR_DOMAIN_POINT; GeometryComponentFieldContext field_context{component, domain}; - const int domain_size = component.attribute_domain_size(domain); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(domain); + if (domain_num == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add(position_field); evaluator.add(offset_field); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc index 3420e17cc10..b98fbd0a0fe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc @@ -17,15 +17,15 @@ static void set_smooth_in_component(GeometryComponent &component, const Field<bool> &shade_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE); + if (domain_num == 0) { return; } OutputAttribute_Typed<bool> shades = component.attribute_try_get_for_output_only<bool>( "shade_smooth", ATTR_DOMAIN_FACE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(shade_field, shades.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc index dc7f3b1343a..976857883f0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc @@ -17,15 +17,15 @@ static void set_cyclic_in_component(GeometryComponent &component, const Field<bool> &cyclic_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE); + if (domain_num == 0) { return; } OutputAttribute_Typed<bool> cyclics = component.attribute_try_get_for_output_only<bool>( "cyclic", ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(cyclic_field, cyclics.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc index f3031ff3678..8b665376c01 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -17,15 +17,15 @@ static void set_resolution_in_component(GeometryComponent &component, const Field<int> &resolution_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE); + if (domain_num == 0) { return; } OutputAttribute_Typed<int> resolutions = component.attribute_try_get_for_output_only<int>( "resolution", ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(resolution_field, resolutions.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc index de206be5367..3b348dd0136 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc @@ -88,8 +88,8 @@ static void try_capture_field_on_geometry(GeometryComponent &component, const GField &field) { GeometryComponentFieldContext field_context{component, domain}; - const int domain_size = component.attribute_domain_size(domain); - const IndexMask mask{IndexMask(domain_size)}; + const int domain_num = component.attribute_domain_num(domain); + const IndexMask mask{IndexMask(domain_num)}; const CPPType &type = field.cpp_type(); const CustomDataType data_type = bke::cpp_type_to_custom_data_type(type); @@ -97,10 +97,10 @@ static void try_capture_field_on_geometry(GeometryComponent &component, /* Could avoid allocating a new buffer if: * - We are writing to an attribute that exists already. * - The field does not depend on that attribute (we can't easily check for that yet). */ - void *buffer = MEM_mallocN(type.size() * domain_size, __func__); + void *buffer = MEM_mallocN(type.size() * domain_num, __func__); fn::FieldEvaluator evaluator{field_context, &mask}; - evaluator.add_with_destination(field, GMutableSpan{type, buffer, domain_size}); + evaluator.add_with_destination(field, GMutableSpan{type, buffer, domain_num}); evaluator.evaluate(); component.attribute_try_delete(name); @@ -114,7 +114,7 @@ static void try_capture_field_on_geometry(GeometryComponent &component, else { /* Cannot change type of built-in attribute. */ } - type.destruct_n(buffer, domain_size); + type.destruct_n(buffer, domain_num); MEM_freeN(buffer); } else { diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 4832feac5bd..9eda5bb34ff 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_task.hh" + #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" @@ -21,7 +23,13 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input<decl::Int>(N_("Level")).default_value(1).min(0).max(6); - b.add_input<decl::Float>(N_("Crease")) + b.add_input<decl::Float>(N_("Edge Crease")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .supports_field() + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Vertex Crease")) .default_value(0.0f) .min(0.0f) .max(1.0f) @@ -44,6 +52,45 @@ static void node_init(bNodeTree *UNUSED(ntree), bNode *node) node->storage = data; } +#ifdef WITH_OPENSUBDIV +static void materialize_and_clamp_creases(const VArray<float> &crease_varray, + MutableSpan<float> creases) +{ + threading::parallel_for(creases.index_range(), 1024, [&](IndexRange range) { + crease_varray.materialize(range, creases); + for (const int i : range) { + creases[i] = std::clamp(creases[i], 0.0f, 1.0f); + } + }); +} + +static void write_vertex_creases(Mesh &mesh, const VArray<float> &crease_varray) +{ + float *crease; + if (CustomData_has_layer(&mesh.vdata, CD_CREASE)) { + crease = static_cast<float *>(CustomData_get_layer(&mesh.vdata, CD_CREASE)); + } + else { + crease = static_cast<float *>( + CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_DEFAULT, nullptr, mesh.totvert)); + } + materialize_and_clamp_creases(crease_varray, {crease, mesh.totvert}); +} + +static void write_edge_creases(MeshComponent &mesh, const VArray<float> &crease_varray) +{ + OutputAttribute_Typed<float> attribute = mesh.attribute_try_get_for_output_only<float>( + "crease", ATTR_DOMAIN_EDGE); + materialize_and_clamp_creases(crease_varray, attribute.as_span()); + attribute.save(); +} + +static bool varray_is_nonzero(const VArray<float> &varray) +{ + return !(varray.is_single() && varray.get_internal_single() == 0.0f); +} +#endif + static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); @@ -51,7 +98,8 @@ static void node_geo_exec(GeoNodeExecParams params) params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenSubdiv")); #else - Field<float> crease_field = params.extract_input<Field<float>>("Crease"); + Field<float> edge_crease_field = params.extract_input<Field<float>>("Edge Crease"); + Field<float> vertex_crease_field = params.extract_input<Field<float>>("Vertex Crease"); const NodeGeometrySubdivisionSurface &storage = node_storage(params.node()); const int uv_smooth = storage.uv_smooth; @@ -69,27 +117,31 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - AttributeDomain domain = ATTR_DOMAIN_EDGE; - GeometryComponentFieldContext field_context{mesh_component, domain}; - const int domain_size = mesh_component.attribute_domain_size(domain); - - if (domain_size == 0) { + const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>(); + const int verts_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT); + const int edges_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE); + if (verts_num == 0 || edges_num == 0) { return; } - FieldEvaluator evaluator(field_context, domain_size); - evaluator.add(crease_field); - evaluator.evaluate(); - const VArray<float> &creases = evaluator.get_evaluated<float>(0); + GeometryComponentFieldContext point_context{mesh_component, ATTR_DOMAIN_POINT}; + FieldEvaluator point_evaluator(point_context, verts_num); + point_evaluator.add(vertex_crease_field); + point_evaluator.evaluate(); + const VArray<float> vertex_creases = point_evaluator.get_evaluated<float>(0); + + GeometryComponentFieldContext edge_context{mesh_component, ATTR_DOMAIN_EDGE}; + FieldEvaluator edge_evaluator(edge_context, edges_num); + edge_evaluator.add(edge_crease_field); + edge_evaluator.evaluate(); + const VArray<float> edge_creases = edge_evaluator.get_evaluated<float>(0); + + const bool use_creases = varray_is_nonzero(vertex_creases) || varray_is_nonzero(edge_creases); - OutputAttribute_Typed<float> crease = mesh_component.attribute_try_get_for_output_only<float>( - "crease", domain); - MutableSpan<float> crease_span = crease.as_span(); - for (auto i : creases.index_range()) { - crease_span[i] = std::clamp(creases[i], 0.0f, 1.0f); + if (use_creases) { + write_vertex_creases(*geometry_set.get_mesh_for_write(), vertex_creases); + write_edge_creases(geometry_set.get_component_for_write<MeshComponent>(), edge_creases); } - crease.save(); /* Initialize mesh settings. */ SubdivToMeshSettings mesh_settings; @@ -100,7 +152,7 @@ static void node_geo_exec(GeoNodeExecParams params) SubdivSettings subdiv_settings; subdiv_settings.is_simple = false; subdiv_settings.is_adaptive = false; - subdiv_settings.use_creases = !(creases.is_single() && creases.get_internal_single() == 0.0f); + subdiv_settings.use_creases = use_creases; subdiv_settings.level = subdiv_level; subdiv_settings.vtx_boundary_interpolation = @@ -108,19 +160,19 @@ static void node_geo_exec(GeoNodeExecParams params) subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( uv_smooth); - Mesh *mesh_in = mesh_component.get_for_write(); + const Mesh &mesh_in = *geometry_set.get_mesh_for_read(); /* Apply subdivision to mesh. */ - Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in); + Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, &mesh_in); /* In case of bad topology, skip to input mesh. */ if (subdiv == nullptr) { return; } - Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); + Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, &mesh_in); - mesh_component.replace(mesh_out); + geometry_set.replace_mesh(mesh_out); BKE_subdiv_free(subdiv); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index 12e306ba480..dca214660c8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -365,7 +365,7 @@ static bool component_is_available(const GeometrySet &geometry, if (component.is_empty()) { return false; } - return component.attribute_domain_size(domain) != 0; + return component.attribute_domain_num(domain) != 0; } /** @@ -433,8 +433,8 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction { { const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>(); source_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); - const int domain_size = mesh_component.attribute_domain_size(domain_); - source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_size); + const int domain_num = mesh_component.attribute_domain_num(domain_); + source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_num); source_evaluator_->add(src_field_); source_evaluator_->evaluate(); source_data_ = &source_evaluator_->get_evaluated(0); @@ -578,9 +578,9 @@ class NearestTransferFunction : public fn::MultiFunction { { if (use_mesh_) { const MeshComponent &mesh = *source_.get_component_for_read<MeshComponent>(); - const int domain_size = mesh.attribute_domain_size(domain_); + const int domain_num = mesh.attribute_domain_num(domain_); mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_)); - mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_size); + mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_num); mesh_evaluator_->add(src_field_); mesh_evaluator_->evaluate(); mesh_data_ = &mesh_evaluator_->get_evaluated(0); @@ -588,9 +588,9 @@ class NearestTransferFunction : public fn::MultiFunction { if (use_points_) { const PointCloudComponent &points = *source_.get_component_for_read<PointCloudComponent>(); - const int domain_size = points.attribute_domain_size(domain_); + const int domain_num = points.attribute_domain_num(domain_); point_context_.emplace(GeometryComponentFieldContext(points, domain_)); - point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_size); + point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_num); point_evaluator_->add(src_field_); point_evaluator_->evaluate(); point_data_ = &point_evaluator_->get_evaluated(0); @@ -658,9 +658,9 @@ class IndexTransferFunction : public fn::MultiFunction { if (component == nullptr) { return; } - const int domain_size = component->attribute_domain_size(domain_); + const int domain_num = component->attribute_domain_num(domain_); geometry_context_.emplace(GeometryComponentFieldContext(*component, domain_)); - evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_size); + evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num); evaluator_->add(src_field_); evaluator_->evaluate(); src_data_ = &evaluator_->get_evaluated(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc index a5ca1cba28f..258c1ac3fba 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc @@ -19,7 +19,7 @@ static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &i { GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - fn::FieldEvaluator evaluator{field_context, instances_component.instances_amount()}; + fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Translation")); evaluator.add(params.extract_input<Field<bool>>("Local Space")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 992470e8279..e47dc22da04 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -83,9 +83,9 @@ static void node_geo_exec(GeoNodeExecParams params) GeometryComponent &component = geometry_set.get_component_for_write<MeshComponent>(); const Mesh &mesh_in = *geometry_set.get_mesh_for_read(); - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE); GeometryComponentFieldContext context{component, ATTR_DOMAIN_FACE}; - FieldEvaluator evaluator{context, domain_size}; + FieldEvaluator evaluator{context, domain_num}; evaluator.add(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 378bac894e8..9a316190720 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -241,27 +241,27 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful case GEO_COMPONENT_TYPE_MESH: { const MeshComponent &mesh_component = *(const MeshComponent *)component; MeshInfo &info = this->mesh_info.emplace(); - info.tot_verts = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - info.tot_edges = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); - info.tot_faces = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE); + info.verts_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT); + info.edges_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE); + info.faces_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_FACE); break; } case GEO_COMPONENT_TYPE_CURVE: { const CurveComponent &curve_component = *(const CurveComponent *)component; CurveInfo &info = this->curve_info.emplace(); - info.tot_splines = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE); + info.splines_num = curve_component.attribute_domain_num(ATTR_DOMAIN_CURVE); break; } case GEO_COMPONENT_TYPE_POINT_CLOUD: { const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component; PointCloudInfo &info = this->pointcloud_info.emplace(); - info.tot_points = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT); + info.points_num = pointcloud_component.attribute_domain_num(ATTR_DOMAIN_POINT); break; } case GEO_COMPONENT_TYPE_INSTANCES: { const InstancesComponent &instances_component = *(const InstancesComponent *)component; InstancesInfo &info = this->instances_info.emplace(); - info.tot_instances = instances_component.instances_amount(); + info.instances_num = instances_component.instances_num(); break; } case GEO_COMPONENT_TYPE_VOLUME: { diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index cea3084a418..39d8c453e43 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -119,14 +119,14 @@ GVArray GeoNodeExecParams::get_input_attribute(const StringRef name, const bNodeSocket *found_socket = this->find_available_socket(name); BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */ const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(type); - const int64_t domain_size = component.attribute_domain_size(domain); + const int64_t domain_num = component.attribute_domain_num(domain); if (default_value == nullptr) { default_value = cpp_type->default_value(); } if (found_socket == nullptr) { - return GVArray::ForSingle(*cpp_type, domain_size, default_value); + return GVArray::ForSingle(*cpp_type, domain_num, default_value); } if (found_socket->type == SOCK_STRING) { @@ -140,40 +140,40 @@ GVArray GeoNodeExecParams::get_input_attribute(const StringRef name, /* If the attribute doesn't exist, use the default value and output an error message * (except when the field is empty, to avoid spamming error messages, and not when * the domain is empty and we don't expect an attribute anyway). */ - if (!name.empty() && component.attribute_domain_size(domain) != 0) { + if (!name.empty() && component.attribute_domain_num(domain) != 0) { this->error_message_add(NodeWarningType::Error, TIP_("No attribute with name \"") + name + "\""); } - return GVArray::ForSingle(*cpp_type, domain_size, default_value); + return GVArray::ForSingle(*cpp_type, domain_num, default_value); } const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions(); if (found_socket->type == SOCK_FLOAT) { const float value = this->get_input<float>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized(CPPType::get<float>(), *cpp_type, &value, buffer); - return GVArray::ForSingle(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_num, buffer); } if (found_socket->type == SOCK_INT) { const int value = this->get_input<int>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized(CPPType::get<int>(), *cpp_type, &value, buffer); - return GVArray::ForSingle(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_num, buffer); } if (found_socket->type == SOCK_VECTOR) { const float3 value = this->get_input<float3>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized(CPPType::get<float3>(), *cpp_type, &value, buffer); - return GVArray::ForSingle(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_num, buffer); } if (found_socket->type == SOCK_RGBA) { const ColorGeometry4f value = this->get_input<ColorGeometry4f>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized( CPPType::get<ColorGeometry4f>(), *cpp_type, &value, buffer); - return GVArray::ForSingle(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_num, buffer); } BLI_assert(false); - return GVArray::ForSingle(*cpp_type, domain_size, default_value); + return GVArray::ForSingle(*cpp_type, domain_num, default_value); } CustomDataType GeoNodeExecParams::get_input_attribute_data_type( diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c index 5d2e1663ae3..e8be093c606 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.c @@ -226,6 +226,39 @@ void node_filter_label(const bNodeTree *UNUSED(ntree), const bNode *node, char * BLI_strncpy(label, IFACE_(name), maxlen); } +void node_combsep_color_label(const ListBase *sockets, NodeCombSepColorMode mode) +{ + bNodeSocket *sock1 = (bNodeSocket *)sockets->first; + bNodeSocket *sock2 = sock1->next; + bNodeSocket *sock3 = sock2->next; + + node_sock_label_clear(sock1); + node_sock_label_clear(sock2); + node_sock_label_clear(sock3); + + switch (mode) { + case NODE_COMBSEP_COLOR_RGB: + node_sock_label(sock1, "Red"); + node_sock_label(sock2, "Green"); + node_sock_label(sock3, "Blue"); + break; + case NODE_COMBSEP_COLOR_HSL: + node_sock_label(sock1, "Hue"); + node_sock_label(sock2, "Saturation"); + node_sock_label(sock3, "Lightness"); + break; + case NODE_COMBSEP_COLOR_HSV: + node_sock_label(sock1, "Hue"); + node_sock_label(sock2, "Saturation"); + node_sock_label(sock3, "Value"); + break; + default: { + BLI_assert_unreachable(); + break; + } + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/nodes/intern/node_util.h b/source/blender/nodes/intern/node_util.h index de03a176c0c..0a2a7a70091 100644 --- a/source/blender/nodes/intern/node_util.h +++ b/source/blender/nodes/intern/node_util.h @@ -65,6 +65,7 @@ void node_filter_label(const struct bNodeTree *ntree, const struct bNode *node, char *label, int maxlen); +void node_combsep_color_label(const ListBase *sockets, NodeCombSepColorMode mode); /*** Link Handling */ diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt index 9b4ea0e0db6..3e90f185168 100644 --- a/source/blender/nodes/shader/CMakeLists.txt +++ b/source/blender/nodes/shader/CMakeLists.txt @@ -82,6 +82,7 @@ set(SRC nodes/node_shader_rgb.cc nodes/node_shader_rgb_to_bw.cc nodes/node_shader_script.cc + nodes/node_shader_sepcomb_color.cc nodes/node_shader_sepcomb_hsv.cc nodes/node_shader_sepcomb_rgb.cc nodes/node_shader_sepcomb_xyz.cc diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index b1db0248d9f..eb47059063d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -28,48 +28,34 @@ static int gpu_shader_curve_vec(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - float *array, layer; - int size; - - CurveMapping *cumap = (CurveMapping *)node->storage; - - BKE_curvemapping_init(cumap); - BKE_curvemapping_table_RGBA(cumap, &array, &size); - GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); - - float ext_xyz[3][4]; - float range_xyz[3]; - - for (int a = 0; a < 3; a++) { - const CurveMap *cm = &cumap->cm[a]; - ext_xyz[a][0] = cm->mintable; - ext_xyz[a][2] = cm->maxtable; - range_xyz[a] = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); - /* Compute extrapolation gradients. */ - if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { - ext_xyz[a][1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_xyz[a])) : - 1e8f; - ext_xyz[a][3] = (cm->ext_out[0] != 0.0f) ? - (cm->ext_out[1] / (cm->ext_out[0] * range_xyz[a])) : - 1e8f; - } - else { - ext_xyz[a][1] = 0.0f; - ext_xyz[a][3] = 0.0f; - } - } + CurveMapping *curve_mapping = (CurveMapping *)node->storage; + + BKE_curvemapping_init(curve_mapping); + float *band_values; + int band_size; + BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size); + float band_layer; + GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer); + + float start_slopes[CM_TOT]; + float end_slopes[CM_TOT]; + BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes); + float range_minimums[CM_TOT]; + BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums); + float range_dividers[CM_TOT]; + BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers); return GPU_stack_link(mat, node, - "curves_vec", + "curves_vector_mixed", in, out, - tex, - GPU_constant(&layer), - GPU_uniform(range_xyz), - GPU_uniform(ext_xyz[0]), - GPU_uniform(ext_xyz[1]), - GPU_uniform(ext_xyz[2])); + band_texture, + GPU_constant(&band_layer), + GPU_uniform(range_minimums), + GPU_uniform(range_dividers), + GPU_uniform(start_slopes), + GPU_uniform(end_slopes)); } class CurveVecFunction : public fn::MultiFunction { @@ -157,72 +143,59 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - float *array, layer; - int size; - bool use_opti = true; - - CurveMapping *cumap = (CurveMapping *)node->storage; - - BKE_curvemapping_init(cumap); - BKE_curvemapping_table_RGBA(cumap, &array, &size); - GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); - - float ext_rgba[4][4]; - float range_rgba[4]; - - for (int a = 0; a < CM_TOT; a++) { - const CurveMap *cm = &cumap->cm[a]; - ext_rgba[a][0] = cm->mintable; - ext_rgba[a][2] = cm->maxtable; - range_rgba[a] = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); - /* Compute extrapolation gradients. */ - if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { - ext_rgba[a][1] = (cm->ext_in[0] != 0.0f) ? - (cm->ext_in[1] / (cm->ext_in[0] * range_rgba[a])) : - 1e8f; - ext_rgba[a][3] = (cm->ext_out[0] != 0.0f) ? - (cm->ext_out[1] / (cm->ext_out[0] * range_rgba[a])) : - 1e8f; - } - else { - ext_rgba[a][1] = 0.0f; - ext_rgba[a][3] = 0.0f; - } - - /* Check if rgb comps are just linear. */ - if (a < 3) { - if (range_rgba[a] != 1.0f || ext_rgba[a][1] != 1.0f || ext_rgba[a][2] != 1.0f || - cm->totpoint != 2 || cm->curve[0].x != 0.0f || cm->curve[0].y != 0.0f || - cm->curve[1].x != 1.0f || cm->curve[1].y != 1.0f) { - use_opti = false; - } - } - } - - if (use_opti) { + CurveMapping *curve_mapping = (CurveMapping *)node->storage; + + BKE_curvemapping_init(curve_mapping); + float *band_values; + int band_size; + BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size); + float band_layer; + GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer); + + float start_slopes[CM_TOT]; + float end_slopes[CM_TOT]; + BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes); + float range_minimums[CM_TOT]; + BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums); + float range_dividers[CM_TOT]; + BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers); + + /* Shader nodes don't do white balancing. */ + float black_level[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + float white_level[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + + /* If the RGB curves do nothing, use a function that skips RGB computations. */ + if (BKE_curvemapping_is_map_identity(curve_mapping, 0) && + BKE_curvemapping_is_map_identity(curve_mapping, 1) && + BKE_curvemapping_is_map_identity(curve_mapping, 2)) { return GPU_stack_link(mat, node, - "curves_rgb_opti", + "curves_combined_only", in, out, - tex, - GPU_constant(&layer), - GPU_uniform(range_rgba), - GPU_uniform(ext_rgba[3])); + GPU_constant(black_level), + GPU_constant(white_level), + band_texture, + GPU_constant(&band_layer), + GPU_uniform(&range_minimums[3]), + GPU_uniform(&range_dividers[3]), + GPU_uniform(&start_slopes[3]), + GPU_uniform(&end_slopes[3])); } return GPU_stack_link(mat, node, - "curves_rgb", + "curves_combined_rgb", in, out, - tex, - GPU_constant(&layer), - GPU_uniform(range_rgba), - GPU_uniform(ext_rgba[0]), - GPU_uniform(ext_rgba[1]), - GPU_uniform(ext_rgba[2]), - GPU_uniform(ext_rgba[3])); + GPU_constant(black_level), + GPU_constant(white_level), + band_texture, + GPU_constant(&band_layer), + GPU_uniform(range_minimums), + GPU_uniform(range_dividers), + GPU_uniform(start_slopes), + GPU_uniform(end_slopes)); } class CurveRGBFunction : public fn::MultiFunction { @@ -316,40 +289,34 @@ static int gpu_shader_curve_float(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - float *array, layer; - int size; - - CurveMapping *cumap = (CurveMapping *)node->storage; + CurveMapping *curve_mapping = (CurveMapping *)node->storage; + + BKE_curvemapping_init(curve_mapping); + float *band_values; + int band_size; + BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size); + float band_layer; + GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer); + + float start_slopes[CM_TOT]; + float end_slopes[CM_TOT]; + BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes); + float range_minimums[CM_TOT]; + BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums); + float range_dividers[CM_TOT]; + BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers); - BKE_curvemapping_init(cumap); - BKE_curvemapping_table_F(cumap, &array, &size); - GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); - - float ext_xyz[4]; - float range_x; - - const CurveMap *cm = &cumap->cm[0]; - ext_xyz[0] = cm->mintable; - ext_xyz[2] = cm->maxtable; - range_x = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); - /* Compute extrapolation gradients. */ - if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { - ext_xyz[1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_x)) : 1e8f; - ext_xyz[3] = (cm->ext_out[0] != 0.0f) ? (cm->ext_out[1] / (cm->ext_out[0] * range_x)) : 1e8f; - } - else { - ext_xyz[1] = 0.0f; - ext_xyz[3] = 0.0f; - } return GPU_stack_link(mat, node, - "curve_float", + "curves_float_mixed", in, out, - tex, - GPU_constant(&layer), - GPU_uniform(&range_x), - GPU_uniform(ext_xyz)); + band_texture, + GPU_constant(&band_layer), + GPU_uniform(range_minimums), + GPU_uniform(range_dividers), + GPU_uniform(start_slopes), + GPU_uniform(end_slopes)); } class CurveFloatFunction : public fn::MultiFunction { diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_color.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_color.cc new file mode 100644 index 00000000000..8e378ebabce --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_color.cc @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup shdnodes + */ + +#include "node_shader_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +static void node_combsep_color_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeCombSepColor *data = MEM_cnew<NodeCombSepColor>(__func__); + data->mode = NODE_COMBSEP_COLOR_RGB; + node->storage = data; +} + +/* **************** SEPARATE COLOR ******************** */ + +namespace blender::nodes::node_shader_separate_color_cc { + +NODE_STORAGE_FUNCS(NodeCombSepColor) + +static void sh_node_sepcolor_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_output<decl::Float>(N_("Red")); + b.add_output<decl::Float>(N_("Green")); + b.add_output<decl::Float>(N_("Blue")); +} + +static void node_sepcolor_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCombSepColor &storage = node_storage(*node); + node_combsep_color_label(&node->outputs, (NodeCombSepColorMode)storage.mode); +} + +static const char *gpu_shader_get_name(int mode) +{ + switch (mode) { + case NODE_COMBSEP_COLOR_RGB: + return "separate_color_rgb"; + case NODE_COMBSEP_COLOR_HSV: + return "separate_color_hsv"; + case NODE_COMBSEP_COLOR_HSL: + return "separate_color_hsl"; + } + + return nullptr; +} + +static int gpu_shader_sepcolor(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + const NodeCombSepColor &storage = node_storage(*node); + const char *name = gpu_shader_get_name(storage.mode); + if (name != nullptr) { + return GPU_stack_link(mat, node, name, in, out); + } + + return 0; +} + +} // namespace blender::nodes::node_shader_separate_color_cc + +void register_node_type_sh_sepcolor() +{ + namespace file_ns = blender::nodes::node_shader_separate_color_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::sh_node_sepcolor_declare; + node_type_update(&ntype, file_ns::node_sepcolor_update); + node_type_init(&ntype, node_combsep_color_init); + node_type_storage( + &ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, file_ns::gpu_shader_sepcolor); + + nodeRegisterType(&ntype); +} + +/* **************** COMBINE COLOR ******************** */ + +namespace blender::nodes::node_shader_combine_color_cc { + +NODE_STORAGE_FUNCS(NodeCombSepColor) + +static void sh_node_combcolor_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Green")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Blue")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_output<decl::Color>(N_("Color")); +} + +static void node_combcolor_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCombSepColor &storage = node_storage(*node); + node_combsep_color_label(&node->inputs, (NodeCombSepColorMode)storage.mode); +} + +static const char *gpu_shader_get_name(int mode) +{ + switch (mode) { + case NODE_COMBSEP_COLOR_RGB: + return "combine_color_rgb"; + case NODE_COMBSEP_COLOR_HSV: + return "combine_color_hsv"; + case NODE_COMBSEP_COLOR_HSL: + return "combine_color_hsl"; + } + + return nullptr; +} + +static int gpu_shader_combcolor(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + const NodeCombSepColor &storage = node_storage(*node); + const char *name = gpu_shader_get_name(storage.mode); + if (name != nullptr) { + return GPU_stack_link(mat, node, name, in, out); + } + + return 0; +} + +} // namespace blender::nodes::node_shader_combine_color_cc + +void register_node_type_sh_combcolor() +{ + namespace file_ns = blender::nodes::node_shader_combine_color_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::sh_node_combcolor_declare; + node_type_update(&ntype, file_ns::node_combcolor_update); + node_type_init(&ntype, node_combsep_color_init); + node_type_storage( + &ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, file_ns::gpu_shader_combcolor); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc index 129c8cf4b97..6dfabe48292 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc @@ -36,7 +36,7 @@ void register_node_type_sh_sephsv() static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_SEPHSV, "Separate HSV", NODE_CLASS_CONVERTER); + sh_node_type_base(&ntype, SH_NODE_SEPHSV_LEGACY, "Separate HSV", NODE_CLASS_CONVERTER); ntype.declare = file_ns::node_declare_sephsv; node_type_gpu(&ntype, file_ns::gpu_shader_sephsv); @@ -72,7 +72,7 @@ void register_node_type_sh_combhsv() static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_COMBHSV, "Combine HSV", NODE_CLASS_CONVERTER); + sh_node_type_base(&ntype, SH_NODE_COMBHSV_LEGACY, "Combine HSV", NODE_CLASS_CONVERTER); ntype.declare = file_ns::node_declare_combhsv; node_type_gpu(&ntype, file_ns::gpu_shader_combhsv); diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc index 657f591a50c..28b55047633 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc @@ -76,7 +76,7 @@ void register_node_type_sh_seprgb() static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB, "Separate RGB", NODE_CLASS_CONVERTER); + sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB_LEGACY, "Separate RGB", NODE_CLASS_CONVERTER); ntype.declare = file_ns::sh_node_seprgb_declare; node_type_gpu(&ntype, file_ns::gpu_shader_seprgb); ntype.build_multi_function = file_ns::sh_node_seprgb_build_multi_function; @@ -119,7 +119,7 @@ void register_node_type_sh_combrgb() static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB, "Combine RGB", NODE_CLASS_CONVERTER); + sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB_LEGACY, "Combine RGB", NODE_CLASS_CONVERTER); ntype.declare = file_ns::sh_node_combrgb_declare; node_type_gpu(&ntype, file_ns::gpu_shader_combrgb); ntype.build_multi_function = file_ns::sh_node_combrgb_build_multi_function; diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc index 06c238fead0..c9588949761 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc @@ -74,7 +74,7 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, const char *gpu_node_name = use_cubic ? "node_tex_tile_cubic" : "node_tex_tile_linear"; GPUNodeLink *gpu_image = GPU_image_tiled(mat, ima, iuser, sampler_state); GPUNodeLink *gpu_image_tile_mapping = GPU_image_tiled_mapping(mat, ima, iuser); - /* UDIM tiles needs a samper2DArray and sampler1DArray for tile mapping. */ + /* UDIM tiles needs a `sampler2DArray` and `sampler1DArray` for tile mapping. */ GPU_stack_link(mat, node, gpu_node_name, in, out, gpu_image, gpu_image_tile_mapping); } else { diff --git a/source/blender/nodes/texture/CMakeLists.txt b/source/blender/nodes/texture/CMakeLists.txt index 7706f118507..5bed54ebfd7 100644 --- a/source/blender/nodes/texture/CMakeLists.txt +++ b/source/blender/nodes/texture/CMakeLists.txt @@ -24,6 +24,7 @@ set(SRC nodes/node_texture_at.c nodes/node_texture_bricks.c nodes/node_texture_checker.c + nodes/node_texture_combine_color.c nodes/node_texture_common.c nodes/node_texture_compose.c nodes/node_texture_coord.c @@ -39,6 +40,7 @@ set(SRC nodes/node_texture_proc.c nodes/node_texture_rotate.c nodes/node_texture_scale.c + nodes/node_texture_separate_color.c nodes/node_texture_texture.c nodes/node_texture_translate.c nodes/node_texture_valToNor.c diff --git a/source/blender/nodes/texture/node_texture_util.c b/source/blender/nodes/texture/node_texture_util.c index 76208104a8c..248114f242a 100644 --- a/source/blender/nodes/texture/node_texture_util.c +++ b/source/blender/nodes/texture/node_texture_util.c @@ -49,7 +49,7 @@ static void tex_call_delegate(TexDelegate *dg, float *out, TexParams *params, sh } } -static void tex_input(float *out, int sz, bNodeStack *in, TexParams *params, short thread) +static void tex_input(float *out, int num, bNodeStack *in, TexParams *params, short thread) { TexDelegate *dg = in->data; if (dg) { @@ -59,7 +59,7 @@ static void tex_input(float *out, int sz, bNodeStack *in, TexParams *params, sho in->vec[1] = in->vec[2] = in->vec[0]; } } - memcpy(out, in->vec, sz * sizeof(float)); + memcpy(out, in->vec, num * sizeof(float)); } void tex_input_vec(float *out, bNodeStack *in, TexParams *params, short thread) diff --git a/source/blender/nodes/texture/nodes/node_texture_combine_color.c b/source/blender/nodes/texture/nodes/node_texture_combine_color.c new file mode 100644 index 00000000000..459553bc950 --- /dev/null +++ b/source/blender/nodes/texture/nodes/node_texture_combine_color.c @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup texnodes + */ + +#include "BLI_listbase.h" +#include "NOD_texture.h" +#include "node_texture_util.h" + +static bNodeSocketTemplate inputs[] = { + {SOCK_FLOAT, N_("Red"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Green"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Blue"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {-1, ""}, +}; +static bNodeSocketTemplate outputs[] = { + {SOCK_RGBA, N_("Color")}, + {-1, ""}, +}; + +static void colorfn(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread) +{ + int i; + for (i = 0; i < 4; i++) { + out[i] = tex_input_value(in[i], p, thread); + } + /* Apply color space if required. */ + switch (node->custom1) { + case NODE_COMBSEP_COLOR_RGB: { + /* Pass */ + break; + } + case NODE_COMBSEP_COLOR_HSV: { + hsv_to_rgb_v(out, out); + break; + } + case NODE_COMBSEP_COLOR_HSL: { + hsl_to_rgb_v(out, out); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } +} + +static void update(bNodeTree *UNUSED(ntree), bNode *node) +{ + node_combsep_color_label(&node->inputs, (NodeCombSepColorMode)node->custom1); +} + +static void exec(void *data, + int UNUSED(thread), + bNode *node, + bNodeExecData *execdata, + bNodeStack **in, + bNodeStack **out) +{ + tex_output(node, execdata, in, out[0], &colorfn, data); +} + +void register_node_type_tex_combine_color(void) +{ + static bNodeType ntype; + + tex_node_type_base(&ntype, TEX_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_OP_COLOR); + node_type_socket_templates(&ntype, inputs, outputs); + node_type_exec(&ntype, NULL, NULL, exec); + node_type_update(&ntype, update); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/texture/nodes/node_texture_compose.c b/source/blender/nodes/texture/nodes/node_texture_compose.c index e341b65ac97..ef14062c72d 100644 --- a/source/blender/nodes/texture/nodes/node_texture_compose.c +++ b/source/blender/nodes/texture/nodes/node_texture_compose.c @@ -42,7 +42,7 @@ void register_node_type_tex_compose(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_COMPOSE, "Combine RGBA", NODE_CLASS_OP_COLOR); + tex_node_type_base(&ntype, TEX_NODE_COMPOSE_LEGACY, "Combine RGBA", NODE_CLASS_OP_COLOR); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_decompose.c b/source/blender/nodes/texture/nodes/node_texture_decompose.c index 21c3944e255..2d42fa4602e 100644 --- a/source/blender/nodes/texture/nodes/node_texture_decompose.c +++ b/source/blender/nodes/texture/nodes/node_texture_decompose.c @@ -62,7 +62,7 @@ void register_node_type_tex_decompose(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_DECOMPOSE, "Separate RGBA", NODE_CLASS_OP_COLOR); + tex_node_type_base(&ntype, TEX_NODE_DECOMPOSE_LEGACY, "Separate RGBA", NODE_CLASS_OP_COLOR); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_separate_color.c b/source/blender/nodes/texture/nodes/node_texture_separate_color.c new file mode 100644 index 00000000000..a482a3f3421 --- /dev/null +++ b/source/blender/nodes/texture/nodes/node_texture_separate_color.c @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup texnodes + */ + +#include "BLI_listbase.h" +#include "NOD_texture.h" +#include "node_texture_util.h" +#include <math.h> + +static bNodeSocketTemplate inputs[] = { + {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f}, + {-1, ""}, +}; +static bNodeSocketTemplate outputs[] = { + {SOCK_FLOAT, N_("Red")}, + {SOCK_FLOAT, N_("Green")}, + {SOCK_FLOAT, N_("Blue")}, + {SOCK_FLOAT, N_("Alpha")}, + {-1, ""}, +}; + +static void apply_color_space(float *out, NodeCombSepColorMode type) +{ + switch (type) { + case NODE_COMBSEP_COLOR_RGB: { + /* Pass */ + break; + } + case NODE_COMBSEP_COLOR_HSV: { + rgb_to_hsv_v(out, out); + break; + } + case NODE_COMBSEP_COLOR_HSL: { + rgb_to_hsl_v(out, out); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } +} + +static void valuefn_r(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread) +{ + tex_input_rgba(out, in[0], p, thread); + apply_color_space(out, (NodeCombSepColorMode)node->custom1); + *out = out[0]; +} + +static void valuefn_g(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread) +{ + tex_input_rgba(out, in[0], p, thread); + apply_color_space(out, (NodeCombSepColorMode)node->custom1); + *out = out[1]; +} + +static void valuefn_b(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread) +{ + tex_input_rgba(out, in[0], p, thread); + apply_color_space(out, (NodeCombSepColorMode)node->custom1); + *out = out[2]; +} + +static void valuefn_a(float *out, TexParams *p, bNode *UNUSED(node), bNodeStack **in, short thread) +{ + tex_input_rgba(out, in[0], p, thread); + *out = out[3]; +} + +static void update(bNodeTree *UNUSED(ntree), bNode *node) +{ + node_combsep_color_label(&node->outputs, (NodeCombSepColorMode)node->custom1); +} + +static void exec(void *data, + int UNUSED(thread), + bNode *node, + bNodeExecData *execdata, + bNodeStack **in, + bNodeStack **out) +{ + tex_output(node, execdata, in, out[0], &valuefn_r, data); + tex_output(node, execdata, in, out[1], &valuefn_g, data); + tex_output(node, execdata, in, out[2], &valuefn_b, data); + tex_output(node, execdata, in, out[3], &valuefn_a, data); +} + +void register_node_type_tex_separate_color(void) +{ + static bNodeType ntype; + + tex_node_type_base(&ntype, TEX_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_OP_COLOR); + node_type_socket_templates(&ntype, inputs, outputs); + node_type_exec(&ntype, NULL, NULL, exec); + node_type_update(&ntype, update); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index baa2b0deb71..972a782d650 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1259,10 +1259,16 @@ static PyObject *bpy_bmesh_select_flush(BPy_BMesh *self, PyObject *value) Py_RETURN_NONE; } -PyDoc_STRVAR(bpy_bmesh_normal_update_doc, - ".. method:: normal_update()\n" - "\n" - " Update mesh normals.\n"); +PyDoc_STRVAR( + bpy_bmesh_normal_update_doc, + ".. method:: normal_update()\n" + "\n" + " Update normals of mesh faces and verts.\n" + "\n" + " .. note::\n" + "\n" + " The normal of any vertex where :attr:`is_wire` is True will be a zero vector.\n"); + static PyObject *bpy_bmesh_normal_update(BPy_BMesh *self) { BPY_BM_CHECK_OBJ(self); @@ -1611,7 +1617,12 @@ static PyObject *bpy_bmvert_calc_shell_factor(BPy_BMVert *self) PyDoc_STRVAR(bpy_bmvert_normal_update_doc, ".. method:: normal_update()\n" "\n" - " Update vertex normal.\n"); + " Update vertex normal.\n" + " This does not update the normals of adjoining faces.\n" + "\n" + " .. note::\n" + "\n" + " The vertex normal will be a zero vector if vertex :attr:`is_wire` is True.\n"); static PyObject *bpy_bmvert_normal_update(BPy_BMVert *self) { BPY_BM_CHECK_OBJ(self); @@ -1773,10 +1784,15 @@ static PyObject *bpy_bmedge_other_vert(BPy_BMEdge *self, BPy_BMVert *value) Py_RETURN_NONE; } -PyDoc_STRVAR(bpy_bmedge_normal_update_doc, - ".. method:: normal_update()\n" - "\n" - " Update edges vertex normals.\n"); +PyDoc_STRVAR( + bpy_bmedge_normal_update_doc, + ".. method:: normal_update()\n" + "\n" + " Update normals of all connected faces and the edge verts.\n" + "\n" + " .. note::\n" + "\n" + " The normal of edge vertex will be a zero vector if vertex :attr:`is_wire` is True.\n"); static PyObject *bpy_bmedge_normal_update(BPy_BMEdge *self) { BPY_BM_CHECK_OBJ(self); @@ -2012,7 +2028,8 @@ static PyObject *bpy_bmface_calc_center_bounds(BPy_BMFace *self) PyDoc_STRVAR(bpy_bmface_normal_update_doc, ".. method:: normal_update()\n" "\n" - " Update face's normal.\n"); + " Update face normal based on the positions of the face verts.\n" + " This does not update the normals of face verts.\n"); static PyObject *bpy_bmface_normal_update(BPy_BMFace *self) { BPY_BM_CHECK_OBJ(self); diff --git a/source/blender/python/generic/blf_py_api.c b/source/blender/python/generic/blf_py_api.c index 9e45105d105..060b7758ea9 100644 --- a/source/blender/python/generic/blf_py_api.c +++ b/source/blender/python/generic/blf_py_api.c @@ -50,7 +50,7 @@ static PyObject *py_blf_position(PyObject *UNUSED(self), PyObject *args) PyDoc_STRVAR(py_blf_size_doc, ".. function:: size(fontid, size, dpi)\n" "\n" - " Set the size and dpi for drawing text.\n" + " Set the size and DPI for drawing text.\n" "\n" " :arg fontid: The id of the typeface as returned by :func:`blf.load`, for default " "font use 0.\n" diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 2fbb6b8ee05..3f880da2f56 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -1279,9 +1279,8 @@ static PyObject *BPy_IDGroup_pop(BPy_IDProperty *self, PyObject *args) pyform = BPy_IDGroup_MapDataToPy(idprop); if (pyform == NULL) { - /* ok something bad happened with the #PyObject, - * so don't remove the prop from the group. if `pyform is - * NULL, then it already should have raised an exception. */ + /* Ok something bad happened with the #PyObject, so don't remove the prop from the group. + * if `pyform` is NULL, then it already should have raised an exception. */ return NULL; } diff --git a/source/blender/python/gpu/CMakeLists.txt b/source/blender/python/gpu/CMakeLists.txt index e726cb7883d..8ccb29beb13 100644 --- a/source/blender/python/gpu/CMakeLists.txt +++ b/source/blender/python/gpu/CMakeLists.txt @@ -29,8 +29,8 @@ set(SRC gpu_py_offscreen.c gpu_py_platform.c gpu_py_select.c - gpu_py_shader_create_info.cc gpu_py_shader.c + gpu_py_shader_create_info.cc gpu_py_state.c gpu_py_texture.c gpu_py_types.c diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index 77333c6dfea..80c48e31510 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -46,6 +46,9 @@ "``3D_FLAT_COLOR``\n" \ " :Attributes: vec3 pos, vec4 color\n" \ " :Uniforms: none\n" \ + "``3D_IMAGE``\n" \ + " :Attributes: vec3 pos, vec2 texCoord\n" \ + " :Uniforms: sampler2D image\n" \ "``3D_SMOOTH_COLOR``\n" \ " :Attributes: vec3 pos, vec4 color\n" \ " :Uniforms: none\n" \ @@ -68,6 +71,7 @@ static const struct PyC_StringEnumItems pygpu_shader_builtin_items[] = { {GPU_SHADER_2D_SMOOTH_COLOR, "2D_SMOOTH_COLOR"}, {GPU_SHADER_2D_UNIFORM_COLOR, "2D_UNIFORM_COLOR"}, {GPU_SHADER_3D_FLAT_COLOR, "3D_FLAT_COLOR"}, + {GPU_SHADER_3D_IMAGE, "3D_IMAGE"}, {GPU_SHADER_3D_SMOOTH_COLOR, "3D_SMOOTH_COLOR"}, {GPU_SHADER_3D_UNIFORM_COLOR, "3D_UNIFORM_COLOR"}, {GPU_SHADER_3D_POLYLINE_FLAT_COLOR, "3D_POLYLINE_FLAT_COLOR"}, diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index 748a501ef76..9d8602d51bd 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -400,6 +400,62 @@ static PyObject *bpy_unescape_identifier(PyObject *UNUSED(self), PyObject *value return value_unescape; } +/** + * \note only exposed for generating documentation, see: `doc/python_api/sphinx_doc_gen.py`. + */ +PyDoc_STRVAR( + bpy_context_members_doc, + ".. function:: context_members()\n" + "\n" + " :return: A dict where the key is the context and the value is a tuple of it's members.\n" + " :rtype: dict\n"); +static PyObject *bpy_context_members(PyObject *UNUSED(self)) +{ + extern const char *buttons_context_dir[]; + extern const char *clip_context_dir[]; + extern const char *file_context_dir[]; + extern const char *image_context_dir[]; + extern const char *node_context_dir[]; + extern const char *screen_context_dir[]; + extern const char *sequencer_context_dir[]; + extern const char *text_context_dir[]; + extern const char *view3d_context_dir[]; + + struct { + const char *name; + const char **dir; + } context_members_all[] = { + {"buttons", buttons_context_dir}, + {"clip", clip_context_dir}, + {"file", file_context_dir}, + {"image", image_context_dir}, + {"node", node_context_dir}, + {"screen", screen_context_dir}, + {"sequencer", sequencer_context_dir}, + {"text", text_context_dir}, + {"view3d", view3d_context_dir}, + }; + + PyObject *result = _PyDict_NewPresized(ARRAY_SIZE(context_members_all)); + for (int context_index = 0; context_index < ARRAY_SIZE(context_members_all); context_index++) { + const char *name = context_members_all[context_index].name; + const char **dir = context_members_all[context_index].dir; + int i; + for (i = 0; dir[i]; i++) { + /* Pass. */ + } + PyObject *members = PyTuple_New(i); + for (i = 0; dir[i]; i++) { + PyTuple_SET_ITEM(members, i, PyUnicode_FromString(dir[i])); + } + PyDict_SetItemString(result, name, members); + Py_DECREF(members); + } + BLI_assert(PyDict_GET_SIZE(result) == ARRAY_SIZE(context_members_all)); + + return result; +} + static PyMethodDef meth_bpy_script_paths = { "script_paths", (PyCFunction)bpy_script_paths, @@ -448,6 +504,12 @@ static PyMethodDef meth_bpy_unescape_identifier = { METH_O, bpy_unescape_identifier_doc, }; +static PyMethodDef meth_bpy_context_members = { + "context_members", + (PyCFunction)bpy_context_members, + METH_NOARGS, + bpy_context_members_doc, +}; static PyObject *bpy_import_test(const char *modname) { @@ -551,6 +613,9 @@ void BPy_init_modules(struct bContext *C) (PyObject *)PyCFunction_New(&meth_bpy_unescape_identifier, NULL)); PyModule_AddObject( mod, meth_bpy_flip_name.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_flip_name, NULL)); + PyModule_AddObject(mod, + meth_bpy_context_members.ml_name, + (PyObject *)PyCFunction_New(&meth_bpy_context_members, NULL)); /* register funcs (bpy_rna.c) */ PyModule_AddObject(mod, diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index e05cfb771c1..5c9de638d8a 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -312,6 +312,14 @@ void BPY_python_start(bContext *C, int argc, const char **argv) PyPreConfig preconfig; PyStatus status; + /* To narrow down reports where the systems Python is inexplicably used, see: T98131. */ + CLOG_INFO( + BPY_LOG_INTERFACE, + 2, + "Initializing %s support for the systems Python environment such as 'PYTHONPATH' and " + "the user-site directory.", + py_use_system_env ? "*with*" : "*without*"); + if (py_use_system_env) { PyPreConfig_InitPythonConfig(&preconfig); } @@ -579,9 +587,9 @@ void BPY_python_backtrace(FILE *fp) PyFrameObject *frame = tstate->frame; do { const int line = PyCode_Addr2Line(frame->f_code, frame->f_lasti); - const char *filename = PyUnicode_AsUTF8(frame->f_code->co_filename); + const char *filepath = PyUnicode_AsUTF8(frame->f_code->co_filename); const char *funcname = PyUnicode_AsUTF8(frame->f_code->co_name); - fprintf(fp, " File \"%s\", line %d in %s\n", filename, line, funcname); + fprintf(fp, " File \"%s\", line %d in %s\n", filepath, line, funcname); } while ((frame = frame->f_back)); } } @@ -770,16 +778,16 @@ static void bpy_module_delay_init(PyObject *bpy_proxy) const char *argv[2]; /* updating the module dict below will lose the reference to __file__ */ - PyObject *filename_obj = PyModule_GetFilenameObject(bpy_proxy); + PyObject *filepath_obj = PyModule_GetFilenameObject(bpy_proxy); - const char *filename_rel = PyUnicode_AsUTF8(filename_obj); /* can be relative */ - char filename_abs[1024]; + const char *filepath_rel = PyUnicode_AsUTF8(filepath_obj); /* can be relative */ + char filepath_abs[1024]; - BLI_strncpy(filename_abs, filename_rel, sizeof(filename_abs)); - BLI_path_abs_from_cwd(filename_abs, sizeof(filename_abs)); - Py_DECREF(filename_obj); + BLI_strncpy(filepath_abs, filepath_rel, sizeof(filepath_abs)); + BLI_path_abs_from_cwd(filepath_abs, sizeof(filepath_abs)); + Py_DECREF(filepath_obj); - argv[0] = filename_abs; + argv[0] = filepath_abs; argv[1] = NULL; // printf("module found %s\n", argv[0]); @@ -810,16 +818,16 @@ PyMODINIT_FUNC PyInit_bpy(void) PyObject *bpy_proxy = PyModule_Create(&bpy_proxy_def); /* Problem: - * 1) this init function is expected to have a private member defined - 'md_def' + * 1) this init function is expected to have a private member defined - `md_def` * but this is only set for C defined modules (not py packages) * so we can't return 'bpy_package_py' as is. * * 2) there is a 'bpy' C module for python to load which is basically all of blender, - * and there is scripts/bpy/__init__.py, + * and there is `scripts/bpy/__init__.py`, * we may end up having to rename this module so there is no naming conflict here eg: * 'from blender import bpy' * - * 3) we don't know the filename at this point, workaround by assigning a dummy value + * 3) we don't know the filepath at this point, workaround by assigning a dummy value * which calls back when its freed so the real loading can take place. */ diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c index e0e5dd869ba..9754599eeed 100644 --- a/source/blender/python/intern/bpy_library_load.c +++ b/source/blender/python/intern/bpy_library_load.c @@ -182,7 +182,7 @@ static PyObject *bpy_lib_load(BPy_PropertyRNA *self, PyObject *args, PyObject *k Main *bmain_base = CTX_data_main(BPY_context_get()); Main *bmain = self->ptr.data; /* Typically #G_MAIN */ BPy_Library *ret; - const char *filename = NULL; + const char *filepath = NULL; bool is_rel = false, is_link = false, use_assets_only = false; static const char *_keywords[] = {"filepath", "link", "relative", "assets_only", NULL}; @@ -200,7 +200,7 @@ static PyObject *bpy_lib_load(BPy_PropertyRNA *self, PyObject *args, PyObject *k if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, - &filename, + &filepath, PyC_ParseBool, &is_link, PyC_ParseBool, @@ -212,8 +212,8 @@ static PyObject *bpy_lib_load(BPy_PropertyRNA *self, PyObject *args, PyObject *k ret = PyObject_New(BPy_Library, &bpy_lib_Type); - BLI_strncpy(ret->relpath, filename, sizeof(ret->relpath)); - BLI_strncpy(ret->abspath, filename, sizeof(ret->abspath)); + BLI_strncpy(ret->relpath, filepath, sizeof(ret->relpath)); + BLI_strncpy(ret->abspath, filepath, sizeof(ret->abspath)); BLI_path_abs(ret->abspath, BKE_main_blendfile_path(bmain)); ret->bmain = bmain; diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 2276e5e97a8..14be9ceda94 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -8886,7 +8886,7 @@ static PyObject *pyrna_register_class(PyObject *UNUSED(self), PyObject *py_class return NULL; } - /* Warning: gets parent classes srna, only for the register function. */ + /* WARNING: gets parent classes srna, only for the register function. */ srna = pyrna_struct_as_srna(py_class, true, "register_class(...):"); if (srna == NULL) { return NULL; diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c index bf876163013..9ffe2879779 100644 --- a/source/blender/render/intern/bake.c +++ b/source/blender/render/intern/bake.c @@ -96,7 +96,7 @@ typedef struct TriTessFace { const MVert *mverts[3]; const float *vert_normals[3]; const TSpace *tspace[3]; - float *loop_normal[3]; + const float *loop_normal[3]; float normal[3]; /* for flat faces */ bool is_smooth; } TriTessFace; @@ -451,9 +451,6 @@ static bool cast_ray_highpoly(BVHTreeFromMesh *treeData, static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval) { int i; - MVert *mvert; - TSpace *tspace = NULL; - float(*loop_normals)[3] = NULL; const int tottri = poly_to_tri_count(me->totpoly, me->totloop); MLoopTri *looptri; @@ -463,7 +460,7 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval unsigned int mpoly_prev = UINT_MAX; float no[3]; - mvert = CustomData_get_layer(&me->vdata, CD_MVERT); + const MVert *mvert = CustomData_get_layer(&me->vdata, CD_MVERT); looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__); triangles = MEM_callocN(sizeof(TriTessFace) * tottri, __func__); @@ -480,6 +477,8 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri); } + const TSpace *tspace = NULL; + const float(*loop_normals)[3] = NULL; if (tangent) { BKE_mesh_ensure_normals_for_display(me_eval); BKE_mesh_calc_normals_split(me_eval); diff --git a/source/blender/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c index f93397eedab..e3229e20595 100644 --- a/source/blender/render/intern/multires_bake.c +++ b/source/blender/render/intern/multires_bake.c @@ -10,6 +10,7 @@ #include "MEM_guardedalloc.h" #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -464,140 +465,141 @@ static void do_multires_bake(MultiresBakeRender *bkr, const MLoopTri *mlooptri = dm->getLoopTriArray(dm); const int lvl = bkr->lvl; int tot_tri = dm->getNumLoopTri(dm); + if (tot_tri < 1) { + return; + } - if (tot_tri > 0) { - MultiresBakeThread *handles; - MultiresBakeQueue queue; - - MVert *mvert = dm->getVertArray(dm); - MPoly *mpoly = dm->getPolyArray(dm); - MLoop *mloop = dm->getLoopArray(dm); - MLoopUV *mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV); - float *pvtangent = NULL; - - ListBase threads; - int i, tot_thread = bkr->threads > 0 ? bkr->threads : BLI_system_thread_count(); - - void *bake_data = NULL; - - Mesh *temp_mesh = BKE_mesh_new_nomain( - dm->getNumVerts(dm), dm->getNumEdges(dm), 0, dm->getNumLoops(dm), dm->getNumPolys(dm)); - memcpy(temp_mesh->mvert, dm->getVertArray(dm), temp_mesh->totvert * sizeof(*temp_mesh->mvert)); - memcpy(temp_mesh->medge, dm->getEdgeArray(dm), temp_mesh->totedge * sizeof(*temp_mesh->medge)); - memcpy(temp_mesh->mpoly, dm->getPolyArray(dm), temp_mesh->totpoly * sizeof(*temp_mesh->mpoly)); - memcpy(temp_mesh->mloop, dm->getLoopArray(dm), temp_mesh->totloop * sizeof(*temp_mesh->mloop)); - const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(temp_mesh); - const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(temp_mesh); - - if (require_tangent) { - if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) { - BKE_mesh_calc_loop_tangent_ex( - dm->getVertArray(dm), - dm->getPolyArray(dm), - dm->getNumPolys(dm), - dm->getLoopArray(dm), - dm->getLoopTriArray(dm), - dm->getNumLoopTri(dm), - &dm->loopData, - true, - NULL, - 0, - vert_normals, - poly_normals, - (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL), - (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */ - /* result */ - &dm->loopData, - dm->getNumLoops(dm), - &dm->tangent_mask); - } - - pvtangent = DM_get_loop_data_layer(dm, CD_TANGENT); + MultiresBakeThread *handles; + MultiresBakeQueue queue; + + MVert *mvert = dm->getVertArray(dm); + MPoly *mpoly = dm->getPolyArray(dm); + MLoop *mloop = dm->getLoopArray(dm); + MLoopUV *mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV); + float *pvtangent = NULL; + + ListBase threads; + int i, tot_thread = bkr->threads > 0 ? bkr->threads : BLI_system_thread_count(); + + void *bake_data = NULL; + + Mesh *temp_mesh = BKE_mesh_new_nomain( + dm->getNumVerts(dm), dm->getNumEdges(dm), 0, dm->getNumLoops(dm), dm->getNumPolys(dm)); + memcpy(temp_mesh->mvert, dm->getVertArray(dm), temp_mesh->totvert * sizeof(*temp_mesh->mvert)); + memcpy(temp_mesh->medge, dm->getEdgeArray(dm), temp_mesh->totedge * sizeof(*temp_mesh->medge)); + memcpy(temp_mesh->mpoly, dm->getPolyArray(dm), temp_mesh->totpoly * sizeof(*temp_mesh->mpoly)); + memcpy(temp_mesh->mloop, dm->getLoopArray(dm), temp_mesh->totloop * sizeof(*temp_mesh->mloop)); + const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(temp_mesh); + const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(temp_mesh); + + if (require_tangent) { + if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) { + BKE_mesh_calc_loop_tangent_ex( + dm->getVertArray(dm), + dm->getPolyArray(dm), + dm->getNumPolys(dm), + dm->getLoopArray(dm), + dm->getLoopTriArray(dm), + dm->getNumLoopTri(dm), + &dm->loopData, + true, + NULL, + 0, + vert_normals, + poly_normals, + (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL), + (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */ + /* result */ + &dm->loopData, + dm->getNumLoops(dm), + &dm->tangent_mask); } - /* all threads shares the same custom bake data */ - if (initBakeData) { - bake_data = initBakeData(bkr, ibuf); - } + pvtangent = DM_get_loop_data_layer(dm, CD_TANGENT); + } - if (tot_thread > 1) { - BLI_threadpool_init(&threads, do_multires_bake_thread, tot_thread); - } + /* all threads shares the same custom bake data */ + if (initBakeData) { + bake_data = initBakeData(bkr, ibuf); + } - handles = MEM_callocN(tot_thread * sizeof(MultiresBakeThread), "do_multires_bake handles"); - - init_ccgdm_arrays(bkr->hires_dm); - - /* faces queue */ - queue.cur_tri = 0; - queue.tot_tri = tot_tri; - BLI_spin_init(&queue.spin); - - /* fill in threads handles */ - for (i = 0; i < tot_thread; i++) { - MultiresBakeThread *handle = &handles[i]; - - handle->bkr = bkr; - handle->image = ima; - handle->queue = &queue; - - handle->data.mpoly = mpoly; - handle->data.mvert = mvert; - handle->data.vert_normals = vert_normals; - handle->data.mloopuv = mloopuv; - BKE_image_get_tile_uv(ima, tile->tile_number, handle->data.uv_offset); - handle->data.mlooptri = mlooptri; - handle->data.mloop = mloop; - handle->data.pvtangent = pvtangent; - handle->data.precomputed_normals = poly_normals; /* don't strictly need this */ - handle->data.w = ibuf->x; - handle->data.h = ibuf->y; - handle->data.lores_dm = dm; - handle->data.hires_dm = bkr->hires_dm; - handle->data.lvl = lvl; - handle->data.pass_data = passKnownData; - handle->data.thread_data = handle; - handle->data.bake_data = bake_data; - handle->data.ibuf = ibuf; - - handle->height_min = FLT_MAX; - handle->height_max = -FLT_MAX; - - init_bake_rast(&handle->bake_rast, ibuf, &handle->data, flush_pixel, bkr->do_update); - - if (tot_thread > 1) { - BLI_threadpool_insert(&threads, handle); - } - } + if (tot_thread > 1) { + BLI_threadpool_init(&threads, do_multires_bake_thread, tot_thread); + } + + handles = MEM_callocN(tot_thread * sizeof(MultiresBakeThread), "do_multires_bake handles"); + + init_ccgdm_arrays(bkr->hires_dm); + + /* faces queue */ + queue.cur_tri = 0; + queue.tot_tri = tot_tri; + BLI_spin_init(&queue.spin); + + /* fill in threads handles */ + for (i = 0; i < tot_thread; i++) { + MultiresBakeThread *handle = &handles[i]; + + handle->bkr = bkr; + handle->image = ima; + handle->queue = &queue; + + handle->data.mpoly = mpoly; + handle->data.mvert = mvert; + handle->data.vert_normals = vert_normals; + handle->data.mloopuv = mloopuv; + BKE_image_get_tile_uv(ima, tile->tile_number, handle->data.uv_offset); + handle->data.mlooptri = mlooptri; + handle->data.mloop = mloop; + handle->data.pvtangent = pvtangent; + handle->data.precomputed_normals = poly_normals; /* don't strictly need this */ + handle->data.w = ibuf->x; + handle->data.h = ibuf->y; + handle->data.lores_dm = dm; + handle->data.hires_dm = bkr->hires_dm; + handle->data.lvl = lvl; + handle->data.pass_data = passKnownData; + handle->data.thread_data = handle; + handle->data.bake_data = bake_data; + handle->data.ibuf = ibuf; + + handle->height_min = FLT_MAX; + handle->height_max = -FLT_MAX; + + init_bake_rast(&handle->bake_rast, ibuf, &handle->data, flush_pixel, bkr->do_update); - /* run threads */ if (tot_thread > 1) { - BLI_threadpool_end(&threads); + BLI_threadpool_insert(&threads, handle); } - else { - do_multires_bake_thread(&handles[0]); - } - - /* construct bake result */ - result->height_min = handles[0].height_min; - result->height_max = handles[0].height_max; + } - for (i = 1; i < tot_thread; i++) { - result->height_min = min_ff(result->height_min, handles[i].height_min); - result->height_max = max_ff(result->height_max, handles[i].height_max); - } + /* run threads */ + if (tot_thread > 1) { + BLI_threadpool_end(&threads); + } + else { + do_multires_bake_thread(&handles[0]); + } - BLI_spin_end(&queue.spin); + /* construct bake result */ + result->height_min = handles[0].height_min; + result->height_max = handles[0].height_max; - /* finalize baking */ - if (freeBakeData) { - freeBakeData(bake_data); - } + for (i = 1; i < tot_thread; i++) { + result->height_min = min_ff(result->height_min, handles[i].height_min); + result->height_max = max_ff(result->height_max, handles[i].height_max); + } - MEM_freeN(handles); + BLI_spin_end(&queue.spin); - BKE_id_free(NULL, temp_mesh); + /* finalize baking */ + if (freeBakeData) { + freeBakeData(bake_data); } + + MEM_freeN(handles); + + BKE_id_free(NULL, temp_mesh); } /* mode = 0: interpolate normals, diff --git a/source/blender/render/intern/texture_image.c b/source/blender/render/intern/texture_image.c index 69c24a18f36..3b1eb293a3a 100644 --- a/source/blender/render/intern/texture_image.c +++ b/source/blender/render/intern/texture_image.c @@ -1215,7 +1215,7 @@ static int imagewraposa_aniso(Tex *tex, AFD.intpol = intpol; AFD.extflag = extflag; - /* brecht: added stupid clamping here, large dx/dy can give very large + /* NOTE(@brecht): added stupid clamping here, large dx/dy can give very large * filter sizes which take ages to render, it may be better to do this * more intelligently later in the code .. probably it's not noticeable */ if (AFD.dxt[0] * AFD.dxt[0] + AFD.dxt[1] * AFD.dxt[1] > 2.0f * 2.0f) { diff --git a/source/blender/render/intern/texture_margin.cc b/source/blender/render/intern/texture_margin.cc index 2d68148a86a..37ef9213615 100644 --- a/source/blender/render/intern/texture_margin.cc +++ b/source/blender/render/intern/texture_margin.cc @@ -12,6 +12,7 @@ #include "BLI_vector.hh" #include "BKE_DerivedMesh.h" +#include "BKE_customdata.h" #include "BKE_mesh.h" #include "DNA_mesh_types.h" @@ -492,11 +493,11 @@ static void generate_margin(ImBuf *ibuf, MPoly *mpoly; MLoop *mloop; - MLoopUV const *mloopuv; + const MLoopUV *mloopuv; int totpoly, totloop, totedge; int tottri; - MLoopTri const *looptri; + const MLoopTri *looptri; MLoopTri *looptri_mem = nullptr; if (me) { @@ -508,11 +509,11 @@ static void generate_margin(ImBuf *ibuf, mloop = me->mloop; if ((uv_layer == nullptr) || (uv_layer[0] == '\0')) { - mloopuv = static_cast<MLoopUV const *>(CustomData_get_layer(&me->ldata, CD_MLOOPUV)); + mloopuv = static_cast<const MLoopUV *>(CustomData_get_layer(&me->ldata, CD_MLOOPUV)); } else { int uv_id = CustomData_get_named_layer(&me->ldata, CD_MLOOPUV, uv_layer); - mloopuv = static_cast<MLoopUV const *>( + mloopuv = static_cast<const MLoopUV *>( CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, uv_id)); } diff --git a/source/blender/render/intern/texture_pointdensity.c b/source/blender/render/intern/texture_pointdensity.c index 8ba3bac7cad..6e0bae700dc 100644 --- a/source/blender/render/intern/texture_pointdensity.c +++ b/source/blender/render/intern/texture_pointdensity.c @@ -271,7 +271,6 @@ static void pointdensity_cache_vertex_color(PointDensity *pd, { const MLoop *mloop = mesh->mloop; const int totloop = mesh->totloop; - const MLoopCol *mcol; char layername[MAX_CUSTOMDATA_LAYER_NAME]; int i; @@ -282,7 +281,7 @@ static void pointdensity_cache_vertex_color(PointDensity *pd, } CustomData_validate_layer_name( &mesh->ldata, CD_PROP_BYTE_COLOR, pd->vertex_attribute_name, layername); - mcol = CustomData_get_layer_named(&mesh->ldata, CD_PROP_BYTE_COLOR, layername); + const MLoopCol *mcol = CustomData_get_layer_named(&mesh->ldata, CD_PROP_BYTE_COLOR, layername); if (!mcol) { return; } @@ -323,13 +322,12 @@ static void pointdensity_cache_vertex_weight(PointDensity *pd, float *data_color) { const int totvert = mesh->totvert; - const MDeformVert *mdef, *dv; int mdef_index; int i; BLI_assert(data_color); - mdef = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); + const MDeformVert *mdef = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); if (!mdef) { return; } @@ -341,6 +339,7 @@ static void pointdensity_cache_vertex_weight(PointDensity *pd, return; } + const MDeformVert *dv; for (i = 0, dv = mdef; i < totvert; i++, dv++, data_color += 3) { MDeformWeight *dw; int j; diff --git a/source/blender/render/intern/texture_procedural.c b/source/blender/render/intern/texture_procedural.c index 73dbcfdfeab..ce58993b7cf 100644 --- a/source/blender/render/intern/texture_procedural.c +++ b/source/blender/render/intern/texture_procedural.c @@ -1208,9 +1208,8 @@ static int multitex(Tex *tex, case TEX_MUSGRAVE: /* newnoise: musgrave types */ - /* ton: added this, for Blender convention reason. - * artificer: added the use of tmpvec to avoid scaling texvec - */ + /* NOTE(@ton): added this, for Blender convention reason. + * NOTE(@artificer): added the use of tmpvec to avoid scaling texvec. */ copy_v3_v3(tmpvec, texvec); mul_v3_fl(tmpvec, 1.0f / tex->noisesize); @@ -1230,18 +1229,16 @@ static int multitex(Tex *tex, break; /* newnoise: voronoi type */ case TEX_VORONOI: - /* ton: added this, for Blender convention reason. - * artificer: added the use of tmpvec to avoid scaling texvec - */ + /* NOTE(@ton): added this, for Blender convention reason. + * NOTE(@artificer): added the use of tmpvec to avoid scaling texvec. */ copy_v3_v3(tmpvec, texvec); mul_v3_fl(tmpvec, 1.0f / tex->noisesize); retval = voronoiTex(tex, tmpvec, texres); break; case TEX_DISTNOISE: - /* ton: added this, for Blender convention reason. - * artificer: added the use of tmpvec to avoid scaling texvec - */ + /* NOTE(@ton): added this, for Blender convention reason. + * NOTE(@artificer): added the use of tmpvec to avoid scaling texvec. */ copy_v3_v3(tmpvec, texvec); mul_v3_fl(tmpvec, 1.0f / tex->noisesize); diff --git a/source/blender/sequencer/intern/disk_cache.c b/source/blender/sequencer/intern/disk_cache.c index 0fdaef61b65..f8e8fc32a5d 100644 --- a/source/blender/sequencer/intern/disk_cache.c +++ b/source/blender/sequencer/intern/disk_cache.c @@ -344,15 +344,15 @@ static void seq_disk_cache_create_version_file(char *path) static void seq_disk_cache_handle_versioning(SeqDiskCache *disk_cache) { - char path[FILE_MAX]; + char filepath[FILE_MAX]; char path_version_file[FILE_MAX]; int version = 0; - seq_disk_cache_get_project_dir(disk_cache, path, sizeof(path)); - BLI_strncpy(path_version_file, path, sizeof(path_version_file)); + seq_disk_cache_get_project_dir(disk_cache, filepath, sizeof(filepath)); + BLI_strncpy(path_version_file, filepath, sizeof(path_version_file)); BLI_path_append(path_version_file, sizeof(path_version_file), "cache_version"); - if (BLI_exists(path) && BLI_is_dir(path)) { + if (BLI_exists(filepath) && BLI_is_dir(filepath)) { FILE *file = BLI_fopen(path_version_file, "r"); if (file) { @@ -364,7 +364,7 @@ static void seq_disk_cache_handle_versioning(SeqDiskCache *disk_cache) } if (version != DCACHE_CURRENT_VERSION) { - BLI_delete(path, false, true); + BLI_delete(filepath, false, true); seq_disk_cache_create_version_file(path_version_file); } } @@ -548,22 +548,22 @@ bool seq_disk_cache_write_file(SeqDiskCache *disk_cache, SeqCacheKey *key, ImBuf { BLI_mutex_lock(&disk_cache->read_write_mutex); - char path[FILE_MAX]; + char filepath[FILE_MAX]; - seq_disk_cache_get_file_path(disk_cache, key, path, sizeof(path)); - BLI_make_existing_file(path); + seq_disk_cache_get_file_path(disk_cache, key, filepath, sizeof(filepath)); + BLI_make_existing_file(filepath); - FILE *file = BLI_fopen(path, "rb+"); + FILE *file = BLI_fopen(filepath, "rb+"); if (!file) { - file = BLI_fopen(path, "wb+"); + file = BLI_fopen(filepath, "wb+"); if (!file) { BLI_mutex_unlock(&disk_cache->read_write_mutex); return false; } - seq_disk_cache_add_file_to_list(disk_cache, path); + seq_disk_cache_add_file_to_list(disk_cache, filepath); } - DiskCacheFile *cache_file = seq_disk_cache_get_file_entry_by_path(disk_cache, path); + DiskCacheFile *cache_file = seq_disk_cache_get_file_entry_by_path(disk_cache, filepath); DiskCacheHeader header; memset(&header, 0, sizeof(header)); /* #BLI_make_existing_file() above may create an empty file. This is fine, don't attempt reading @@ -585,7 +585,7 @@ bool seq_disk_cache_write_file(SeqDiskCache *disk_cache, SeqCacheKey *key, ImBuf */ header.entry[entry_index].size_compressed = bytes_written; seq_disk_cache_write_header(file, &header); - seq_disk_cache_update_file(disk_cache, path); + seq_disk_cache_update_file(disk_cache, filepath); fclose(file); BLI_mutex_unlock(&disk_cache->read_write_mutex); @@ -600,13 +600,13 @@ ImBuf *seq_disk_cache_read_file(SeqDiskCache *disk_cache, SeqCacheKey *key) { BLI_mutex_lock(&disk_cache->read_write_mutex); - char path[FILE_MAX]; + char filepath[FILE_MAX]; DiskCacheHeader header; - seq_disk_cache_get_file_path(disk_cache, key, path, sizeof(path)); - BLI_make_existing_file(path); + seq_disk_cache_get_file_path(disk_cache, key, filepath, sizeof(filepath)); + BLI_make_existing_file(filepath); - FILE *file = BLI_fopen(path, "rb"); + FILE *file = BLI_fopen(filepath, "rb"); if (!file) { BLI_mutex_unlock(&disk_cache->read_write_mutex); return NULL; @@ -656,8 +656,8 @@ ImBuf *seq_disk_cache_read_file(SeqDiskCache *disk_cache, SeqCacheKey *key) BLI_mutex_unlock(&disk_cache->read_write_mutex); return NULL; } - BLI_file_touch(path); - seq_disk_cache_update_file(disk_cache, path); + BLI_file_touch(filepath); + seq_disk_cache_update_file(disk_cache, filepath); fclose(file); BLI_mutex_unlock(&disk_cache->read_write_mutex); diff --git a/source/blender/simulation/intern/SIM_mass_spring.cpp b/source/blender/simulation/intern/SIM_mass_spring.cpp index 46597f80d75..f5a6427a6a4 100644 --- a/source/blender/simulation/intern/SIM_mass_spring.cpp +++ b/source/blender/simulation/intern/SIM_mass_spring.cpp @@ -268,9 +268,8 @@ static void cloth_setup_constraints(ClothModifierData *clmd) /** * Computes where the cloth would be if it were subject to perfectly stiff edges - * (edge distance constraints) in a lagrangian solver. then add forces to help - * guide the implicit solver to that state. this function is called after - * collisions. + * (edge distance constraints) in a lagrangian solver. Then add forces to help + * guide the implicit solver to that state. This function is called after collisions. */ static int UNUSED_FUNCTION(cloth_calc_helper_forces)(Object *UNUSED(ob), ClothModifierData *clmd, diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 525301d4393..268382ec923 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -1196,7 +1196,7 @@ struct ID *WM_drag_get_local_ID_from_event(const struct wmEvent *event, short id bool WM_drag_is_ID_type(const struct wmDrag *drag, int idcode); /** - * \note: Does not store \a asset in any way, so it's fine to pass a temporary. + * \note Does not store \a asset in any way, so it's fine to pass a temporary. */ wmDragAsset *WM_drag_create_asset_data(const struct AssetHandle *asset, struct AssetMetaData *metadata, @@ -1228,7 +1228,7 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, struct wmDragAssetCatalog *WM_drag_get_asset_catalog_data(const struct wmDrag *drag); /** - * \note: Does not store \a asset in any way, so it's fine to pass a temporary. + * \note Does not store \a asset in any way, so it's fine to pass a temporary. */ void WM_drag_add_asset_list_item(wmDrag *drag, const struct bContext *C, diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index a9b8d91ca03..9e9f195c430 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -812,6 +812,10 @@ typedef struct wmXrActionData { char action_set[64]; /** Action name. */ char action[64]; + /** User path. E.g. "/user/hand/left" */ + char user_path[64]; + /** Other user path, for bimanual actions. E.g. "/user/hand/right" */ + char user_path_other[64]; /** Type. */ eXrActionType type; /** State. Set appropriately based on type. */ @@ -1193,8 +1197,9 @@ typedef struct wmDropBox { struct wmDrag *drag, const int xy[2]); - /** Called with the draw buffer (#GPUViewport) set up for drawing into the region's view. - * \note Only setups the drawing buffer for drawing in view, not the GPU transform matricies. + /** + * Called with the draw buffer (#GPUViewport) set up for drawing into the region's view. + * \note Only setups the drawing buffer for drawing in view, not the GPU transform matrices. * The callback has to do that itself, with for example #UI_view2d_view_ortho. * \param xy: Cursor location in window coordinates (#wmEvent.xy compatible). */ diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 4b8552a4ec3..3ff3117dafe 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -101,6 +101,7 @@ static int wm_operator_call_internal(bContext *C, static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot); static wmEvent *wm_event_add_mousemove_to_head(wmWindow *win); +static void wm_operator_free_for_fileselect(wmOperator *file_operator); static void wm_event_state_update_and_click_set_ex(wmEvent *event, wmEvent *event_state, @@ -1899,8 +1900,15 @@ void wm_event_free_handler(wmEventHandler *handler) MEM_freeN(handler); } -/* Only set context when area/region is part of screen. */ -static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const wmEvent *event) +/** + * Check if the handler's area and/or region are actually part of the screen, and return them if + * so. + */ +static void wm_handler_op_context_get_if_valid(bContext *C, + wmEventHandler_Op *handler, + const wmEvent *event, + ScrArea **r_area, + ARegion **r_region) { wmWindow *win = handler->context.win ? handler->context.win : CTX_wm_window(C); /* It's probably fine to always use #WM_window_get_active_screen() to get the screen. But this @@ -1908,12 +1916,15 @@ static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const * possible. */ bScreen *screen = handler->context.win ? WM_window_get_active_screen(win) : CTX_wm_screen(C); + *r_area = NULL; + *r_region = NULL; + if (screen == NULL || handler->op == NULL) { return; } if (handler->context.area == NULL) { - CTX_wm_area_set(C, NULL); + /* Pass */ } else { ScrArea *area = NULL; @@ -1937,7 +1948,7 @@ static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const else { ARegion *region; wmOperator *op = handler->op ? (handler->op->opm ? handler->op->opm : handler->op) : NULL; - CTX_wm_area_set(C, area); + *r_area = area; if (op && (op->flag & OP_IS_MODAL_CURSOR_REGION)) { region = BKE_area_find_region_xy(area, handler->context.region_type, event->xy); @@ -1949,23 +1960,29 @@ static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const region = NULL; } - if (region == NULL) { - LISTBASE_FOREACH (ARegion *, region_iter, &area->regionbase) { - region = region_iter; - if (region == handler->context.region) { - break; - } + if ((region == NULL) && handler->context.region) { + if (BLI_findindex(&area->regionbase, handler->context.region) != -1) { + region = handler->context.region; } } /* No warning print here, after full-area and back regions are remade. */ if (region) { - CTX_wm_region_set(C, region); + *r_region = region; } } } } +static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const wmEvent *event) +{ + ScrArea *area = NULL; + ARegion *region = NULL; + wm_handler_op_context_get_if_valid(C, handler, event, &area, ®ion); + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); +} + void WM_event_remove_handlers(bContext *C, ListBase *handlers) { wmWindowManager *wm = CTX_wm_manager(C); @@ -2012,7 +2029,13 @@ void WM_event_remove_handlers(bContext *C, ListBase *handlers) } WM_cursor_grab_disable(win, NULL); - WM_operator_free(handler->op); + + if (handler->is_fileselect) { + wm_operator_free_for_fileselect(handler->op); + } + else { + WM_operator_free(handler->op); + } } } else if (handler_base->type == WM_HANDLER_TYPE_UI) { @@ -2449,6 +2472,22 @@ static int wm_handler_operator_call(bContext *C, return WM_HANDLER_BREAK; } +static void wm_operator_free_for_fileselect(wmOperator *file_operator) +{ + LISTBASE_FOREACH (bScreen *, screen, &G_MAIN->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + if (area->spacetype == SPACE_FILE) { + SpaceFile *sfile = area->spacedata.first; + if (sfile->op == file_operator) { + sfile->op = NULL; + } + } + } + } + + WM_operator_free(file_operator); +} + /** * File-select handlers are only in the window queue, * so it's safe to switch screens or area types. @@ -2502,6 +2541,9 @@ static int wm_handler_fileselect_do(bContext *C, case EVT_FILESELECT_CANCEL: case EVT_FILESELECT_EXTERNAL_CANCEL: { wmWindow *ctx_win = CTX_wm_window(C); + wmEvent *eventstate = ctx_win->eventstate; + /* The root window of the operation as determined in #WM_event_add_fileselect(). */ + wmWindow *root_win = handler->context.win; /* Remove link now, for load file case before removing. */ BLI_remlink(handlers, handler); @@ -2537,21 +2579,17 @@ static int wm_handler_fileselect_do(bContext *C, ED_fileselect_params_to_userdef(file_area->spacedata.first, win_size, is_maximized); if (BLI_listbase_is_single(&file_area->spacedata)) { - BLI_assert(ctx_win != win); + BLI_assert(root_win != win); wm_window_close(C, wm, win); - CTX_wm_window_set(C, ctx_win); /* #wm_window_close() NULLs. */ + CTX_wm_window_set(C, root_win); /* #wm_window_close() NULLs. */ /* Some operators expect a drawable context (for #EVT_FILESELECT_EXEC). */ - wm_window_make_drawable(wm, ctx_win); + wm_window_make_drawable(wm, root_win); /* Ensure correct cursor position, otherwise, popups may close immediately after * opening (#UI_BLOCK_MOVEMOUSE_QUIT). */ - wm_cursor_position_get( - ctx_win, &ctx_win->eventstate->xy[0], &ctx_win->eventstate->xy[1]); - wm->winactive = ctx_win; /* Reports use this... */ - if (handler->context.win == win) { - handler->context.win = NULL; - } + wm_cursor_position_get(root_win, &eventstate->xy[0], &eventstate->xy[1]); + wm->winactive = root_win; /* Reports use this... */ } else if (file_area->full) { ED_screen_full_prevspace(C, file_area); @@ -2570,7 +2608,13 @@ static int wm_handler_fileselect_do(bContext *C, } } - wm_handler_op_context(C, handler, ctx_win->eventstate); + CTX_wm_window_set(C, root_win); + wm_handler_op_context(C, handler, eventstate); + /* At this point context is supposed to match the root context determined by + * #WM_event_add_fileselect(). */ + BLI_assert(!CTX_wm_area(C) || (CTX_wm_area(C) == handler->context.area)); + BLI_assert(!CTX_wm_region(C) || (CTX_wm_region(C) == handler->context.region)); + ScrArea *handler_area = CTX_wm_area(C); /* Make sure new context area is ready, the operator callback may operate on it. */ if (handler_area) { @@ -2639,7 +2683,7 @@ static int wm_handler_fileselect_do(bContext *C, } if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) { - WM_operator_free(handler->op); + wm_operator_free_for_fileselect(handler->op); } } else { @@ -2654,8 +2698,7 @@ static int wm_handler_fileselect_do(bContext *C, wm->op_undo_depth--; } } - - WM_operator_free(handler->op); + wm_operator_free_for_fileselect(handler->op); } CTX_wm_area_set(C, NULL); @@ -3976,58 +4019,122 @@ void WM_event_fileselect_event(wmWindowManager *wm, void *ophandle, int eventval event.type = EVT_FILESELECT; event.val = eventval; + event.flag = 0; event.customdata = ophandle; /* Only as void pointer type check. */ wm_event_add(win, &event); } } +/** + * From the context window, try to find a window that is appropriate for use as root window of a + * modal File Browser (modal means: there is a #SpaceFile.op to execute). The root window will + * become the parent of the File Browser and provides a context to execute the file operator in, + * even after closing the File Browser. + * + * An appropriate window is either of the following: + * * A parent window that does not yet contain a modal File Browser. This is determined using + * #ED_fileselect_handler_area_find_any_with_op(). + * * A parent window containing a modal File Browser, but in a maximized/fullscreen state. Users + * shouldn't be able to put a temporary screen like the modal File Browser into + * maximized/fullscreen state themselves. So this setup indicates that the File Browser was + * opened using #USER_TEMP_SPACE_DISPLAY_FULLSCREEN. + * + * If no appropriate parent window can be found from the context window, return the first + * registered window (which can be assumed to be a regular window, e.g. no modal File Browser; this + * is asserted). + */ +static wmWindow *wm_event_find_fileselect_root_window_from_context(const bContext *C) +{ + wmWindow *ctx_win = CTX_wm_window(C); + + for (wmWindow *ctx_win_or_parent = ctx_win; ctx_win_or_parent; + ctx_win_or_parent = ctx_win_or_parent->parent) { + ScrArea *file_area = ED_fileselect_handler_area_find_any_with_op(ctx_win_or_parent); + + if (!file_area) { + return ctx_win_or_parent; + } + + if (file_area->full) { + return ctx_win_or_parent; + } + } + + /* Fallback to the first window. */ + const wmWindowManager *wm = CTX_wm_manager(C); + BLI_assert(!ED_fileselect_handler_area_find_any_with_op(wm->windows.first)); + return wm->windows.first; +} + /* Operator is supposed to have a filled "path" property. */ /* Optional property: file-type (XXX enum?) */ void WM_event_add_fileselect(bContext *C, wmOperator *op) { wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *win = CTX_wm_window(C); - const bool is_temp_screen = WM_window_is_temp_screen(win); + wmWindow *ctx_win = CTX_wm_window(C); + + /* The following vars define the root context. That is essentially the "parent" context of the + * File Browser operation, to be restored for eventually executing the file operation. */ + wmWindow *root_win = wm_event_find_fileselect_root_window_from_context(C); + /* Determined later. */ + ScrArea *root_area = NULL; + ARegion *root_region = NULL; /* Close any popups, like when opening a file browser from the splash. */ - UI_popup_handlers_remove_all(C, &win->modalhandlers); + UI_popup_handlers_remove_all(C, &root_win->modalhandlers); - if (!is_temp_screen) { - /* Only allow 1 file selector open per window. */ - LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, &win->modalhandlers) { - if (handler_base->type == WM_HANDLER_TYPE_OP) { - wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base; - if (handler->is_fileselect == false) { - continue; - } + /* Setting the context window unsets the context area & screen. Avoid doing that, so operators + * calling the file browser can operate in the context the browser was opened in. */ + if (ctx_win != root_win) { + CTX_wm_window_set(C, root_win); + } - ScrArea *file_area = ED_fileselect_handler_area_find(win, handler->op); + /* The root window may already have a File Browser open. Cancel it if so, only 1 should be open + * per window. The root context of this operation is also used for the new operation. */ + LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, &root_win->modalhandlers) { + if (handler_base->type == WM_HANDLER_TYPE_OP) { + wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base; + if (handler->is_fileselect == false) { + continue; + } - if (file_area) { - CTX_wm_area_set(C, file_area); - wm_handler_fileselect_do(C, &win->modalhandlers, handler, EVT_FILESELECT_CANCEL); - } - /* If not found we stop the handler without changing the screen. */ - else { - wm_handler_fileselect_do( - C, &win->modalhandlers, handler, EVT_FILESELECT_EXTERNAL_CANCEL); - } + wm_handler_op_context_get_if_valid( + C, handler, ctx_win->eventstate, &root_area, &root_region); + + ScrArea *file_area = ED_fileselect_handler_area_find(root_win, handler->op); + + if (file_area) { + CTX_wm_area_set(C, file_area); + wm_handler_fileselect_do(C, &root_win->modalhandlers, handler, EVT_FILESELECT_CANCEL); + } + /* If not found we stop the handler without changing the screen. */ + else { + wm_handler_fileselect_do( + C, &root_win->modalhandlers, handler, EVT_FILESELECT_EXTERNAL_CANCEL); } } } + BLI_assert(root_win != NULL); + /* When not reusing the root context from a previous file browsing operation, use the current + * area & region, if they are inside the root window. */ + if (!root_area && ctx_win == root_win) { + root_area = CTX_wm_area(C); + root_region = CTX_wm_region(C); + } + wmEventHandler_Op *handler = MEM_callocN(sizeof(*handler), __func__); handler->head.type = WM_HANDLER_TYPE_OP; handler->is_fileselect = true; handler->op = op; - handler->context.win = CTX_wm_window(C); - handler->context.area = CTX_wm_area(C); - handler->context.region = CTX_wm_region(C); + handler->context.win = root_win; + handler->context.area = root_area; + handler->context.region = root_region; - BLI_addhead(&win->modalhandlers, handler); + BLI_addhead(&root_win->modalhandlers, handler); /* Check props once before invoking if check is available * ensures initial properties are valid. */ @@ -4036,6 +4143,10 @@ void WM_event_add_fileselect(bContext *C, wmOperator *op) } WM_event_fileselect_event(wm, op, EVT_FILESELECT_FULL_OPEN); + + if (ctx_win != root_win) { + CTX_wm_window_set(C, ctx_win); + } } /** \} */ @@ -5735,6 +5846,7 @@ void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win) wmEvent test_event = *win->eventstate; test_event.type = event_data[data_index].event_type; test_event.val = event_data[data_index].event_value; + test_event.flag = 0; wm_eventemulation(&test_event, true); wmKeyMapItem *kmi = NULL; for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) { @@ -5801,7 +5913,7 @@ bool WM_window_modal_keymap_status_draw(bContext *C, wmWindow *win, uiLayout *la bool show_text = true; { - /* Warning: O(n^2). */ + /* WARNING: O(n^2). */ wmKeyMapItem *kmi = NULL; for (kmi = keymap->items.first; kmi; kmi = kmi->next) { if (kmi->propvalue == items[i].value) { diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 0a76fd0a25f..2a829e274d9 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -1025,6 +1025,10 @@ static wmXrActionData *wm_xr_session_event_create(const char *action_set_name, wmXrActionData *data = MEM_callocN(sizeof(wmXrActionData), __func__); strcpy(data->action_set, action_set_name); strcpy(data->action, action->name); + strcpy(data->user_path, action->subaction_paths[subaction_idx]); + if (bimanual) { + strcpy(data->user_path_other, action->subaction_paths[subaction_idx_other]); + } data->type = action->type; switch (action->type) { diff --git a/tests/python/bl_rigging_symmetrize.py b/tests/python/bl_rigging_symmetrize.py index cac5e9b0856..6531b4df85f 100644 --- a/tests/python/bl_rigging_symmetrize.py +++ b/tests/python/bl_rigging_symmetrize.py @@ -134,7 +134,8 @@ def check_constraints(self, input_arm, expected_arm, bone, exp_bone): "Missmatching constraint boolean in pose.bones[%s].constraints[%s].%s" % ( bone.name, const_name, var)) else: - msg = "Missmatching constraint value in pose.bones[%s].constraints[%s].%s" % (bone.name, const_name, var) + msg = "Missmatching constraint value in pose.bones[%s].constraints[%s].%s" % ( + bone.name, const_name, var) self.assertAlmostEqual(value, exp_value, places=6, msg=msg) |