Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-tidy9
-rw-r--r--CMakeLists.txt13
-rw-r--r--build_files/build_environment/cmake/download.cmake2
-rw-r--r--build_files/cmake/Modules/FindUSD.cmake1
-rw-r--r--build_files/cmake/Modules/FindWebP.cmake5
-rw-r--r--build_files/cmake/platform/platform_win32.cmake2
-rwxr-xr-xbuild_files/cmake/project_info.py2
-rw-r--r--build_files/config/pipeline_config.yaml18
-rw-r--r--doc/doxygen/Doxyfile2
-rw-r--r--doc/python_api/examples/bpy.types.Image.py1
-rw-r--r--doc/python_api/examples/gpu.6.py33
-rw-r--r--extern/ceres/CMakeLists.txt661
-rw-r--r--extern/ceres/ChangeLog596
-rw-r--r--extern/ceres/LICENSE234
-rw-r--r--extern/ceres/README3
-rw-r--r--extern/ceres/README.blender2
-rw-r--r--extern/ceres/README.md18
-rwxr-xr-xextern/ceres/bundle.sh165
-rw-r--r--extern/ceres/config/ceres/internal/config.h101
-rw-r--r--extern/ceres/config/ceres/internal/export.h42
-rw-r--r--extern/ceres/files.txt318
-rw-r--r--extern/ceres/include/ceres/autodiff_cost_function.h7
-rw-r--r--extern/ceres/include/ceres/autodiff_first_order_function.h8
-rw-r--r--extern/ceres/include/ceres/autodiff_local_parameterization.h12
-rw-r--r--extern/ceres/include/ceres/autodiff_manifold.h259
-rw-r--r--extern/ceres/include/ceres/c_api.h2
-rw-r--r--extern/ceres/include/ceres/ceres.h10
-rw-r--r--extern/ceres/include/ceres/conditioned_cost_function.h6
-rw-r--r--extern/ceres/include/ceres/context.h8
-rw-r--r--extern/ceres/include/ceres/cost_function.h10
-rw-r--r--extern/ceres/include/ceres/cost_function_to_functor.h3
-rw-r--r--extern/ceres/include/ceres/covariance.h5
-rw-r--r--extern/ceres/include/ceres/crs_matrix.h8
-rw-r--r--extern/ceres/include/ceres/cubic_interpolation.h26
-rw-r--r--extern/ceres/include/ceres/dynamic_autodiff_cost_function.h18
-rw-r--r--extern/ceres/include/ceres/dynamic_cost_function.h5
-rw-r--r--extern/ceres/include/ceres/dynamic_cost_function_to_functor.h10
-rw-r--r--extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h11
-rw-r--r--extern/ceres/include/ceres/evaluation_callback.h4
-rw-r--r--extern/ceres/include/ceres/first_order_function.h4
-rw-r--r--extern/ceres/include/ceres/gradient_checker.h73
-rw-r--r--extern/ceres/include/ceres/gradient_problem.h103
-rw-r--r--extern/ceres/include/ceres/gradient_problem_solver.h5
-rw-r--r--extern/ceres/include/ceres/internal/array_selector.h8
-rw-r--r--extern/ceres/include/ceres/internal/autodiff.h15
-rw-r--r--extern/ceres/include/ceres/internal/eigen.h46
-rw-r--r--extern/ceres/include/ceres/internal/householder_vector.h8
-rw-r--r--extern/ceres/include/ceres/internal/integer_sequence_algorithm.h123
-rw-r--r--extern/ceres/include/ceres/internal/jet_traits.h223
-rw-r--r--extern/ceres/include/ceres/internal/numeric_diff.h34
-rw-r--r--extern/ceres/include/ceres/internal/port.h116
-rw-r--r--extern/ceres/include/ceres/internal/sphere_manifold_functions.h162
-rw-r--r--extern/ceres/include/ceres/internal/variadic_evaluate.h3
-rw-r--r--extern/ceres/include/ceres/iteration_callback.h5
-rw-r--r--extern/ceres/include/ceres/jet.h537
-rw-r--r--extern/ceres/include/ceres/jet_fwd.h (renamed from extern/ceres/internal/ceres/split.h)28
-rw-r--r--extern/ceres/include/ceres/line_manifold.h304
-rw-r--r--extern/ceres/include/ceres/local_parameterization.h66
-rw-r--r--extern/ceres/include/ceres/loss_function.h48
-rw-r--r--extern/ceres/include/ceres/manifold.h411
-rw-r--r--extern/ceres/include/ceres/manifold_test_utils.h328
-rw-r--r--extern/ceres/include/ceres/normal_prior.h2
-rw-r--r--extern/ceres/include/ceres/numeric_diff_cost_function.h11
-rw-r--r--extern/ceres/include/ceres/numeric_diff_first_order_function.h163
-rw-r--r--extern/ceres/include/ceres/numeric_diff_options.h5
-rw-r--r--extern/ceres/include/ceres/ordered_groups.h4
-rw-r--r--extern/ceres/include/ceres/problem.h544
-rw-r--r--extern/ceres/include/ceres/product_manifold.h328
-rw-r--r--extern/ceres/include/ceres/rotation.h30
-rw-r--r--extern/ceres/include/ceres/sized_cost_function.h2
-rw-r--r--extern/ceres/include/ceres/solver.h40
-rw-r--r--extern/ceres/include/ceres/sphere_manifold.h231
-rw-r--r--extern/ceres/include/ceres/tiny_solver.h102
-rw-r--r--extern/ceres/include/ceres/tiny_solver_autodiff_function.h6
-rw-r--r--extern/ceres/include/ceres/tiny_solver_cost_function_adapter.h6
-rw-r--r--extern/ceres/include/ceres/types.h3
-rw-r--r--extern/ceres/include/ceres/version.h4
-rw-r--r--extern/ceres/internal/ceres/accelerate_sparse.cc23
-rw-r--r--extern/ceres/internal/ceres/accelerate_sparse.h4
-rw-r--r--extern/ceres/internal/ceres/array_utils.cc8
-rw-r--r--extern/ceres/internal/ceres/array_utils.h21
-rw-r--r--extern/ceres/internal/ceres/block_evaluate_preparer.cc4
-rw-r--r--extern/ceres/internal/ceres/block_evaluate_preparer.h3
-rw-r--r--extern/ceres/internal/ceres/block_jacobi_preconditioner.cc15
-rw-r--r--extern/ceres/internal/ceres/block_jacobi_preconditioner.h9
-rw-r--r--extern/ceres/internal/ceres/block_jacobian_writer.cc32
-rw-r--r--extern/ceres/internal/ceres/block_jacobian_writer.h10
-rw-r--r--extern/ceres/internal/ceres/block_random_access_dense_matrix.cc6
-rw-r--r--extern/ceres/internal/ceres/block_random_access_dense_matrix.h11
-rw-r--r--extern/ceres/internal/ceres/block_random_access_diagonal_matrix.cc22
-rw-r--r--extern/ceres/internal/ceres/block_random_access_diagonal_matrix.h9
-rw-r--r--extern/ceres/internal/ceres/block_random_access_matrix.cc2
-rw-r--r--extern/ceres/internal/ceres/block_random_access_matrix.h16
-rw-r--r--extern/ceres/internal/ceres/block_random_access_sparse_matrix.cc15
-rw-r--r--extern/ceres/internal/ceres/block_random_access_sparse_matrix.h11
-rw-r--r--extern/ceres/internal/ceres/block_sparse_matrix.cc86
-rw-r--r--extern/ceres/internal/ceres/block_sparse_matrix.h19
-rw-r--r--extern/ceres/internal/ceres/block_structure.h36
-rw-r--r--extern/ceres/internal/ceres/c_api.cc42
-rw-r--r--extern/ceres/internal/ceres/callbacks.cc63
-rw-r--r--extern/ceres/internal/ceres/callbacks.h15
-rw-r--r--extern/ceres/internal/ceres/canonical_views_clustering.cc19
-rw-r--r--extern/ceres/internal/ceres/canonical_views_clustering.h9
-rw-r--r--extern/ceres/internal/ceres/casts.h8
-rw-r--r--extern/ceres/internal/ceres/cgnr_linear_operator.h9
-rw-r--r--extern/ceres/internal/ceres/cgnr_solver.cc15
-rw-r--r--extern/ceres/internal/ceres/cgnr_solver.h7
-rw-r--r--extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.cc2
-rw-r--r--extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.h10
-rw-r--r--extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc51
-rw-r--r--extern/ceres/internal/ceres/compressed_row_jacobian_writer.h9
-rw-r--r--extern/ceres/internal/ceres/compressed_row_sparse_matrix.cc52
-rw-r--r--extern/ceres/internal/ceres/compressed_row_sparse_matrix.h34
-rw-r--r--extern/ceres/internal/ceres/concurrent_queue.h4
-rw-r--r--extern/ceres/internal/ceres/conditioned_cost_function.cc2
-rw-r--r--extern/ceres/internal/ceres/conjugate_gradients_solver.cc7
-rw-r--r--extern/ceres/internal/ceres/conjugate_gradients_solver.h9
-rw-r--r--extern/ceres/internal/ceres/context.cc2
-rw-r--r--extern/ceres/internal/ceres/context_impl.cc66
-rw-r--r--extern/ceres/internal/ceres/context_impl.h40
-rw-r--r--extern/ceres/internal/ceres/coordinate_descent_minimizer.cc53
-rw-r--r--extern/ceres/internal/ceres/coordinate_descent_minimizer.h7
-rw-r--r--extern/ceres/internal/ceres/corrector.cc6
-rw-r--r--extern/ceres/internal/ceres/corrector.h7
-rw-r--r--extern/ceres/internal/ceres/cost_function.cc (renamed from extern/ceres/internal/ceres/blas.h)28
-rw-r--r--extern/ceres/internal/ceres/covariance.cc5
-rw-r--r--extern/ceres/internal/ceres/covariance_impl.cc136
-rw-r--r--extern/ceres/internal/ceres/covariance_impl.h7
-rw-r--r--extern/ceres/internal/ceres/cuda_buffer.h107
-rw-r--r--extern/ceres/internal/ceres/cxsparse.cc37
-rw-r--r--extern/ceres/internal/ceres/cxsparse.h17
-rw-r--r--extern/ceres/internal/ceres/dense_cholesky.cc327
-rw-r--r--extern/ceres/internal/ceres/dense_cholesky.h183
-rw-r--r--extern/ceres/internal/ceres/dense_jacobian_writer.h27
-rw-r--r--extern/ceres/internal/ceres/dense_normal_cholesky_solver.cc86
-rw-r--r--extern/ceres/internal/ceres/dense_normal_cholesky_solver.h25
-rw-r--r--extern/ceres/internal/ceres/dense_qr.cc481
-rw-r--r--extern/ceres/internal/ceres/dense_qr.h207
-rw-r--r--extern/ceres/internal/ceres/dense_qr_solver.cc112
-rw-r--r--extern/ceres/internal/ceres/dense_qr_solver.h12
-rw-r--r--extern/ceres/internal/ceres/dense_sparse_matrix.cc90
-rw-r--r--extern/ceres/internal/ceres/dense_sparse_matrix.h47
-rw-r--r--extern/ceres/internal/ceres/detect_structure.h15
-rw-r--r--extern/ceres/internal/ceres/dogleg_strategy.cc2
-rw-r--r--extern/ceres/internal/ceres/dogleg_strategy.h14
-rw-r--r--extern/ceres/internal/ceres/dynamic_compressed_row_finalizer.h5
-rw-r--r--extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.cc30
-rw-r--r--extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.h10
-rw-r--r--extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.cc6
-rw-r--r--extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.h7
-rw-r--r--extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc27
-rw-r--r--extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.h9
-rw-r--r--extern/ceres/internal/ceres/eigensparse.cc65
-rw-r--r--extern/ceres/internal/ceres/eigensparse.h35
-rw-r--r--extern/ceres/internal/ceres/evaluation_callback.cc37
-rw-r--r--extern/ceres/internal/ceres/evaluator.cc33
-rw-r--r--extern/ceres/internal/ceres/evaluator.h34
-rw-r--r--extern/ceres/internal/ceres/execution_summary.h15
-rw-r--r--extern/ceres/internal/ceres/file.h11
-rw-r--r--extern/ceres/internal/ceres/first_order_function.cc (renamed from extern/ceres/internal/ceres/blas.cc)51
-rw-r--r--extern/ceres/internal/ceres/float_cxsparse.cc4
-rw-r--r--extern/ceres/internal/ceres/float_cxsparse.h5
-rw-r--r--extern/ceres/internal/ceres/float_suitesparse.cc4
-rw-r--r--extern/ceres/internal/ceres/float_suitesparse.h5
-rw-r--r--extern/ceres/internal/ceres/function_sample.h7
-rw-r--r--extern/ceres/internal/ceres/generate_template_specializations.py246
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_2.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_3.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_4.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_d.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_3.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_4.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_6.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_9.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_d.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_3.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_4.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_6.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_8.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_9.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_d.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_d_d.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_3_3_3.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_2.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_3.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_4.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_d.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_2_2.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_2_3.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_2_4.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_2_d.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_3_3.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_3_4.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_3_6.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_3_9.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_3_d.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_4_3.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_4_4.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_4_6.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_4_8.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_4_9.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_4_d.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_2_d_d.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_3_3_3.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_4_4_2.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_4_4_3.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_4_4_4.cc2
-rw-r--r--extern/ceres/internal/ceres/generated/schur_eliminator_4_4_d.cc2
-rw-r--r--extern/ceres/internal/ceres/gradient_checker.cc107
-rw-r--r--extern/ceres/internal/ceres/gradient_checking_cost_function.cc83
-rw-r--r--extern/ceres/internal/ceres/gradient_checking_cost_function.h19
-rw-r--r--extern/ceres/internal/ceres/gradient_problem.cc46
-rw-r--r--extern/ceres/internal/ceres/gradient_problem_evaluator.h18
-rw-r--r--extern/ceres/internal/ceres/gradient_problem_solver.cc17
-rw-r--r--extern/ceres/internal/ceres/graph.h7
-rw-r--r--extern/ceres/internal/ceres/graph_algorithms.h8
-rw-r--r--extern/ceres/internal/ceres/implicit_schur_complement.cc28
-rw-r--r--extern/ceres/internal/ceres/implicit_schur_complement.h8
-rw-r--r--extern/ceres/internal/ceres/inner_product_computer.cc22
-rw-r--r--extern/ceres/internal/ceres/inner_product_computer.h13
-rw-r--r--extern/ceres/internal/ceres/is_close.h21
-rw-r--r--extern/ceres/internal/ceres/iteration_callback.cc37
-rw-r--r--extern/ceres/internal/ceres/iterative_refiner.cc2
-rw-r--r--extern/ceres/internal/ceres/iterative_refiner.h7
-rw-r--r--extern/ceres/internal/ceres/iterative_schur_complement_solver.cc31
-rw-r--r--extern/ceres/internal/ceres/iterative_schur_complement_solver.h11
-rw-r--r--extern/ceres/internal/ceres/lapack.cc190
-rw-r--r--extern/ceres/internal/ceres/lapack.h101
-rw-r--r--extern/ceres/internal/ceres/levenberg_marquardt_strategy.cc2
-rw-r--r--extern/ceres/internal/ceres/levenberg_marquardt_strategy.h9
-rw-r--r--extern/ceres/internal/ceres/line_search.cc28
-rw-r--r--extern/ceres/internal/ceres/line_search.h24
-rw-r--r--extern/ceres/internal/ceres/line_search_direction.cc39
-rw-r--r--extern/ceres/internal/ceres/line_search_direction.h32
-rw-r--r--extern/ceres/internal/ceres/line_search_minimizer.cc10
-rw-r--r--extern/ceres/internal/ceres/line_search_minimizer.h4
-rw-r--r--extern/ceres/internal/ceres/line_search_preprocessor.cc14
-rw-r--r--extern/ceres/internal/ceres/line_search_preprocessor.h8
-rw-r--r--extern/ceres/internal/ceres/linear_least_squares_problems.cc185
-rw-r--r--extern/ceres/internal/ceres/linear_least_squares_problems.h29
-rw-r--r--extern/ceres/internal/ceres/linear_operator.cc2
-rw-r--r--extern/ceres/internal/ceres/linear_operator.h4
-rw-r--r--extern/ceres/internal/ceres/linear_solver.cc34
-rw-r--r--extern/ceres/internal/ceres/linear_solver.h19
-rw-r--r--extern/ceres/internal/ceres/local_parameterization.cc8
-rw-r--r--extern/ceres/internal/ceres/loss_function.cc4
-rw-r--r--extern/ceres/internal/ceres/low_rank_inverse_hessian.cc4
-rw-r--r--extern/ceres/internal/ceres/low_rank_inverse_hessian.h4
-rw-r--r--extern/ceres/internal/ceres/manifold.cc321
-rw-r--r--extern/ceres/internal/ceres/manifold_adapter.h60
-rw-r--r--extern/ceres/internal/ceres/map_util.h4
-rw-r--r--extern/ceres/internal/ceres/minimizer.cc12
-rw-r--r--extern/ceres/internal/ceres/minimizer.h9
-rw-r--r--extern/ceres/internal/ceres/normal_prior.cc4
-rw-r--r--extern/ceres/internal/ceres/pair_hash.h6
-rw-r--r--extern/ceres/internal/ceres/parallel_for.h23
-rw-r--r--extern/ceres/internal/ceres/parallel_for_cxx.cc6
-rw-r--r--extern/ceres/internal/ceres/parallel_for_nothreads.cc6
-rw-r--r--extern/ceres/internal/ceres/parallel_for_openmp.cc6
-rw-r--r--extern/ceres/internal/ceres/parallel_utils.h10
-rw-r--r--extern/ceres/internal/ceres/parameter_block.h142
-rw-r--r--extern/ceres/internal/ceres/parameter_block_ordering.cc33
-rw-r--r--extern/ceres/internal/ceres/parameter_block_ordering.h16
-rw-r--r--extern/ceres/internal/ceres/partitioned_matrix_view.cc96
-rw-r--r--extern/ceres/internal/ceres/partitioned_matrix_view.h31
-rw-r--r--extern/ceres/internal/ceres/partitioned_matrix_view_impl.h69
-rw-r--r--extern/ceres/internal/ceres/partitioned_matrix_view_template.py151
-rw-r--r--extern/ceres/internal/ceres/polynomial.cc27
-rw-r--r--extern/ceres/internal/ceres/polynomial.h33
-rw-r--r--extern/ceres/internal/ceres/preconditioner.cc5
-rw-r--r--extern/ceres/internal/ceres/preconditioner.h31
-rw-r--r--extern/ceres/internal/ceres/preprocessor.cc24
-rw-r--r--extern/ceres/internal/ceres/preprocessor.h17
-rw-r--r--extern/ceres/internal/ceres/problem.cc37
-rw-r--r--extern/ceres/internal/ceres/problem_impl.cc185
-rw-r--r--extern/ceres/internal/ceres/problem_impl.h63
-rw-r--r--extern/ceres/internal/ceres/program.cc142
-rw-r--r--extern/ceres/internal/ceres/program.h29
-rw-r--r--extern/ceres/internal/ceres/program_evaluator.h47
-rw-r--r--extern/ceres/internal/ceres/random.h4
-rw-r--r--extern/ceres/internal/ceres/reorder_program.cc37
-rw-r--r--extern/ceres/internal/ceres/reorder_program.h17
-rw-r--r--extern/ceres/internal/ceres/residual_block.cc30
-rw-r--r--extern/ceres/internal/ceres/residual_block.h23
-rw-r--r--extern/ceres/internal/ceres/residual_block_utils.cc10
-rw-r--r--extern/ceres/internal/ceres/residual_block_utils.h7
-rw-r--r--extern/ceres/internal/ceres/schur_complement_solver.cc93
-rw-r--r--extern/ceres/internal/ceres/schur_complement_solver.h43
-rw-r--r--extern/ceres/internal/ceres/schur_eliminator.cc53
-rw-r--r--extern/ceres/internal/ceres/schur_eliminator.h32
-rw-r--r--extern/ceres/internal/ceres/schur_eliminator_impl.h33
-rw-r--r--extern/ceres/internal/ceres/schur_eliminator_template.py151
-rw-r--r--extern/ceres/internal/ceres/schur_jacobi_preconditioner.cc13
-rw-r--r--extern/ceres/internal/ceres/schur_jacobi_preconditioner.h13
-rw-r--r--extern/ceres/internal/ceres/schur_templates.h3
-rw-r--r--extern/ceres/internal/ceres/scoped_thread_token.h10
-rw-r--r--extern/ceres/internal/ceres/scratch_evaluate_preparer.cc15
-rw-r--r--extern/ceres/internal/ceres/scratch_evaluate_preparer.h11
-rw-r--r--extern/ceres/internal/ceres/single_linkage_clustering.h13
-rw-r--r--extern/ceres/internal/ceres/small_blas.h18
-rw-r--r--extern/ceres/internal/ceres/small_blas_generic.h20
-rw-r--r--extern/ceres/internal/ceres/solver.cc36
-rw-r--r--extern/ceres/internal/ceres/solver_utils.cc9
-rw-r--r--extern/ceres/internal/ceres/solver_utils.h10
-rw-r--r--extern/ceres/internal/ceres/sparse_cholesky.cc6
-rw-r--r--extern/ceres/internal/ceres/sparse_cholesky.h33
-rw-r--r--extern/ceres/internal/ceres/sparse_matrix.cc2
-rw-r--r--extern/ceres/internal/ceres/sparse_matrix.h14
-rw-r--r--extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc18
-rw-r--r--extern/ceres/internal/ceres/sparse_normal_cholesky_solver.h9
-rw-r--r--extern/ceres/internal/ceres/split.cc122
-rw-r--r--extern/ceres/internal/ceres/stl_util.h2
-rw-r--r--extern/ceres/internal/ceres/stringprintf.cc4
-rw-r--r--extern/ceres/internal/ceres/stringprintf.h24
-rw-r--r--extern/ceres/internal/ceres/subset_preconditioner.cc21
-rw-r--r--extern/ceres/internal/ceres/subset_preconditioner.h11
-rw-r--r--extern/ceres/internal/ceres/suitesparse.cc5
-rw-r--r--extern/ceres/internal/ceres/suitesparse.h29
-rw-r--r--extern/ceres/internal/ceres/thread_pool.cc6
-rw-r--r--extern/ceres/internal/ceres/thread_pool.h4
-rw-r--r--extern/ceres/internal/ceres/thread_token_provider.h10
-rw-r--r--extern/ceres/internal/ceres/triplet_sparse_matrix.cc44
-rw-r--r--extern/ceres/internal/ceres/triplet_sparse_matrix.h19
-rw-r--r--extern/ceres/internal/ceres/trust_region_minimizer.cc20
-rw-r--r--extern/ceres/internal/ceres/trust_region_minimizer.h9
-rw-r--r--extern/ceres/internal/ceres/trust_region_preprocessor.cc41
-rw-r--r--extern/ceres/internal/ceres/trust_region_preprocessor.h8
-rw-r--r--extern/ceres/internal/ceres/trust_region_step_evaluator.h4
-rw-r--r--extern/ceres/internal/ceres/trust_region_strategy.cc13
-rw-r--r--extern/ceres/internal/ceres/trust_region_strategy.h10
-rw-r--r--extern/ceres/internal/ceres/types.cc12
-rw-r--r--extern/ceres/internal/ceres/visibility.cc27
-rw-r--r--extern/ceres/internal/ceres/visibility.h10
-rw-r--r--extern/ceres/internal/ceres/visibility_based_preconditioner.cc63
-rw-r--r--extern/ceres/internal/ceres/visibility_based_preconditioner.h11
-rw-r--r--extern/ceres/internal/ceres/wall_time.cc4
-rw-r--r--extern/ceres/internal/ceres/wall_time.h9
-rwxr-xr-xextern/ceres/mkfiles.sh5
-rw-r--r--extern/ceres/patches/series0
-rw-r--r--intern/cycles/blender/image.cpp15
-rw-r--r--intern/cycles/blender/image.h8
-rw-r--r--intern/cycles/blender/mesh.cpp2
-rw-r--r--intern/cycles/blender/shader.cpp32
-rw-r--r--intern/cycles/device/metal/device_impl.h6
-rw-r--r--intern/cycles/device/metal/device_impl.mm96
-rw-r--r--intern/cycles/device/metal/kernel.h108
-rw-r--r--intern/cycles/device/metal/kernel.mm856
-rw-r--r--intern/cycles/device/metal/queue.mm31
-rw-r--r--intern/cycles/device/optix/device_impl.cpp38
-rw-r--r--intern/cycles/kernel/CMakeLists.txt1
-rw-r--r--intern/cycles/kernel/device/metal/compat.h24
-rw-r--r--intern/cycles/kernel/osl/services.cpp38
-rw-r--r--intern/cycles/kernel/osl/services.h14
-rw-r--r--intern/cycles/kernel/osl/shaders/CMakeLists.txt2
-rw-r--r--intern/cycles/kernel/osl/shaders/node_color.h50
-rw-r--r--intern/cycles/kernel/osl/shaders/node_combine_color.osl16
-rw-r--r--intern/cycles/kernel/osl/shaders/node_separate_color.osl26
-rw-r--r--intern/cycles/kernel/svm/color_util.h26
-rw-r--r--intern/cycles/kernel/svm/sepcomb_color.h54
-rw-r--r--intern/cycles/kernel/svm/svm.h7
-rw-r--r--intern/cycles/kernel/svm/types.h8
-rw-r--r--intern/cycles/scene/image.cpp51
-rw-r--r--intern/cycles/scene/image.h9
-rw-r--r--intern/cycles/scene/osl.cpp7
-rw-r--r--intern/cycles/scene/osl.h2
-rw-r--r--intern/cycles/scene/shader_nodes.cpp139
-rw-r--r--intern/cycles/scene/shader_nodes.h20
-rw-r--r--intern/cycles/util/color.h50
-rw-r--r--intern/ghost/CMakeLists.txt2
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp66
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.h10
-rw-r--r--intern/ghost/intern/GHOST_TrackpadWin32.cpp343
-rw-r--r--intern/ghost/intern/GHOST_TrackpadWin32.h138
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.cpp72
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.h21
-rw-r--r--intern/libmv/CMakeLists.txt1
-rwxr-xr-xintern/libmv/bundle.sh1
-rw-r--r--intern/libmv/libmv/simple_pipeline/bundle.cc17
-rw-r--r--intern/libmv/libmv/simple_pipeline/modal_solver.cc9
-rw-r--r--release/datafiles/blender_icons.svg32
-rw-r--r--release/datafiles/blender_icons16/icon16_geometry_nodes.datbin0 -> 1048 bytes
-rw-r--r--release/datafiles/blender_icons32/icon32_geometry_nodes.datbin0 -> 4120 bytes
-rw-r--r--release/datafiles/icons/ops.curves.sculpt_puff.datbin0 -> 1718 bytes
-rw-r--r--release/datafiles/splash.pngbin1113821 -> 842492 bytes
-rw-r--r--release/scripts/modules/bpy_extras/__init__.py1
-rw-r--r--release/scripts/modules/bpy_extras/id_map_utils.py49
-rw-r--r--release/scripts/modules/bpy_types.py8
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py6
-rw-r--r--release/scripts/startup/bl_ui/properties_data_curve.py2
-rw-r--r--release/scripts/startup/bl_ui/properties_grease_pencil_common.py32
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py48
-rw-r--r--release/scripts/startup/bl_ui/properties_particle.py9
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_field.py16
-rw-r--r--release/scripts/startup/bl_ui/properties_texture.py1
-rw-r--r--release/scripts/startup/bl_ui/space_image.py28
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py1
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py121
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py85
-rw-r--r--release/scripts/startup/nodeitems_builtins.py24
-rw-r--r--source/CMakeLists.txt5
-rw-r--r--source/blender/blenfont/intern/blf_font.c2
-rw-r--r--source/blender/blenfont/intern/blf_internal_types.h7
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h6
-rw-r--r--source/blender/blenkernel/BKE_colortools.h30
-rw-r--r--source/blender/blenkernel/BKE_curves.hh53
-rw-r--r--source/blender/blenkernel/BKE_curves_utils.hh26
-rw-r--r--source/blender/blenkernel/BKE_geometry_fields.hh10
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh16
-rw-r--r--source/blender/blenkernel/BKE_image.h2
-rw-r--r--source/blender/blenkernel/BKE_lib_principle_properties.h2
-rw-r--r--source/blender/blenkernel/BKE_material.h2
-rw-r--r--source/blender/blenkernel/BKE_mesh.h1
-rw-r--r--source/blender/blenkernel/BKE_modifier.h3
-rw-r--r--source/blender/blenkernel/BKE_node.h36
-rw-r--r--source/blender/blenkernel/BKE_paint.h2
-rw-r--r--source/blender/blenkernel/BKE_spline.hh24
-rw-r--r--source/blender/blenkernel/BKE_subdiv.h32
-rw-r--r--source/blender/blenkernel/BKE_tracking.h153
-rw-r--r--source/blender/blenkernel/CMakeLists.txt4
-rw-r--r--source/blender/blenkernel/intern/anonymous_attribute.cc2
-rw-r--r--source/blender/blenkernel/intern/asset_catalog_test.cc2
-rw-r--r--source/blender/blenkernel/intern/attribute.c4
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc106
-rw-r--r--source/blender/blenkernel/intern/attribute_access_intern.hh16
-rw-r--r--source/blender/blenkernel/intern/blendfile_link_append.c2
-rw-r--r--source/blender/blenkernel/intern/brush.c1
-rw-r--r--source/blender/blenkernel/intern/colortools.c74
-rw-r--r--source/blender/blenkernel/intern/curve_catmull_rom.cc8
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc6
-rw-r--r--source/blender/blenkernel/intern/curve_nurbs.cc48
-rw-r--r--source/blender/blenkernel/intern/curve_poly.cc4
-rw-r--r--source/blender/blenkernel/intern/curve_to_mesh_convert.cc19
-rw-r--r--source/blender/blenkernel/intern/curves.cc27
-rw-r--r--source/blender/blenkernel/intern/curves_geometry.cc152
-rw-r--r--source/blender/blenkernel/intern/curves_utils.cc38
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc28
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curves.cc65
-rw-r--r--source/blender/blenkernel/intern/geometry_component_instances.cc14
-rw-r--r--source/blender/blenkernel/intern/geometry_component_mesh.cc12
-rw-r--r--source/blender/blenkernel/intern/geometry_component_pointcloud.cc2
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc2
-rw-r--r--source/blender/blenkernel/intern/image.cc123
-rw-r--r--source/blender/blenkernel/intern/main.c6
-rw-r--r--source/blender/blenkernel/intern/material.c7
-rw-r--r--source/blender/blenkernel/intern/mesh.cc13
-rw-r--r--source/blender/blenkernel/intern/node.cc8
-rw-r--r--source/blender/blenkernel/intern/object.cc2
-rw-r--r--source/blender/blenkernel/intern/packedFile.c24
-rw-r--r--source/blender/blenkernel/intern/pbvh_pixels.cc2
-rw-r--r--source/blender/blenkernel/intern/spline_base.cc58
-rw-r--r--source/blender/blenkernel/intern/spline_bezier.cc46
-rw-r--r--source/blender/blenkernel/intern/spline_nurbs.cc56
-rw-r--r--source/blender/blenkernel/intern/spline_poly.cc2
-rw-r--r--source/blender/blenkernel/intern/subdiv.c20
-rw-r--r--source/blender/blenkernel/intern/subdiv_converter_mesh.c20
-rw-r--r--source/blender/blenkernel/intern/subdiv_eval.c12
-rw-r--r--source/blender/blenkernel/intern/tracking.c223
-rw-r--r--source/blender/blenkernel/intern/tracking_plane_tracker.c6
-rw-r--r--source/blender/blenlib/BLI_fileops.h2
-rw-r--r--source/blender/blenlib/BLI_float3x3.hh192
-rw-r--r--source/blender/blenlib/BLI_length_parameterize.hh2
-rw-r--r--source/blender/blenlib/BLI_math_rotation.h1
-rw-r--r--source/blender/blenlib/BLI_string.h2
-rw-r--r--source/blender/blenlib/BLI_task.hh18
-rw-r--r--source/blender/blenlib/CMakeLists.txt6
-rw-r--r--source/blender/blenlib/intern/BLI_filelist.c4
-rw-r--r--source/blender/blenlib/intern/math_color.c2
-rw-r--r--source/blender/blenlib/intern/string.c2
-rw-r--r--source/blender/blenlib/tests/BLI_float3x3_test.cc119
-rw-r--r--source/blender/blenlib/tests/BLI_path_util_test.cc4
-rw-r--r--source/blender/blenlib/tests/BLI_string_test.cc86
-rw-r--r--source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc12
-rw-r--r--source/blender/blenloader/intern/readfile.c18
-rw-r--r--source/blender/blenloader/intern/versioning_300.c273
-rw-r--r--source/blender/bmesh/bmesh_class.h16
-rw-r--r--source/blender/compositor/CMakeLists.txt4
-rw-r--r--source/blender/compositor/intern/COM_Converter.cc24
-rw-r--r--source/blender/compositor/nodes/COM_CombineColorNode.cc59
-rw-r--r--source/blender/compositor/nodes/COM_CombineColorNode.h39
-rw-r--r--source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc78
-rw-r--r--source/blender/compositor/nodes/COM_CombineColorNodeLegacy.h56
-rw-r--r--source/blender/compositor/nodes/COM_SeparateColorNode.cc59
-rw-r--r--source/blender/compositor/nodes/COM_SeparateColorNode.h39
-rw-r--r--source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc110
-rw-r--r--source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.h56
-rw-r--r--source/blender/compositor/operations/COM_ConvertOperation.cc62
-rw-r--r--source/blender/compositor/operations/COM_ConvertOperation.h20
-rw-r--r--source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc10
-rw-r--r--source/blender/depsgraph/DEG_depsgraph_query.h2
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query.cc69
-rw-r--r--source/blender/draw/CMakeLists.txt2
-rw-r--r--source/blender/draw/engines/eevee/eevee_cryptomatte.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightcache.c12
-rw-r--r--source/blender/draw/engines/eevee/eevee_lookdev.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h4
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_defines.hh8
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_instance.cc4
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_material.hh6
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader.cc10
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader.hh2
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader_shared.hh2
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_sync.hh2
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_view.cc10
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_view.hh4
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_world.hh2
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh2
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_render.c12
-rw-r--r--source/blender/draw/engines/overlay/overlay_gpencil.c8
-rw-r--r--source/blender/draw/engines/overlay/shaders/infos/extra_info.hh2
-rw-r--r--source/blender/draw/engines/overlay/shaders/infos/volume_info.hh2
-rw-r--r--source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh2
-rw-r--r--source/blender/draw/engines/select/shaders/infos/select_id_info.hh2
-rw-r--r--source/blender/draw/engines/workbench/workbench_render.c6
-rw-r--r--source/blender/draw/engines/workbench/workbench_volume.c22
-rw-r--r--source/blender/draw/intern/DRW_render.h4
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curve.cc2
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curves.cc73
-rw-r--r--source/blender/draw/intern/draw_curves.cc2
-rw-r--r--source/blender/draw/intern/draw_manager.c2
-rw-r--r--source/blender/draw/intern/draw_manager.h2
-rw-r--r--source/blender/draw/intern/draw_manager_data.c12
-rw-r--r--source/blender/draw/intern/draw_subdivision.h4
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc2
-rw-r--r--source/blender/draw/intern/shaders/common_globals_lib.glsl2
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c5
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c5
-rw-r--r--source/blender/editors/animation/keyframes_draw.c38
-rw-r--r--source/blender/editors/curve/CMakeLists.txt2
-rw-r--r--source/blender/editors/curves/CMakeLists.txt5
-rw-r--r--source/blender/editors/curves/intern/curves_add.cc2
-rw-r--r--source/blender/editors/curves/intern/curves_ops.cc138
-rw-r--r--source/blender/editors/datafiles/CMakeLists.txt1
-rw-r--r--source/blender/editors/geometry/geometry_attributes.cc12
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c166
-rw-r--r--source/blender/editors/include/ED_fileselect.h8
-rw-r--r--source/blender/editors/include/ED_uvedit.h12
-rw-r--r--source/blender/editors/include/ED_view3d.h16
-rw-r--r--source/blender/editors/include/UI_icons.h2
-rw-r--r--source/blender/editors/include/UI_interface.h27
-rw-r--r--source/blender/editors/include/UI_interface_icons.h2
-rw-r--r--source/blender/editors/interface/interface.cc58
-rw-r--r--source/blender/editors/interface/interface_style.cc2
-rw-r--r--source/blender/editors/interface/view2d_gizmo_navigate.cc7
-rw-r--r--source/blender/editors/io/io_alembic.c3
-rw-r--r--source/blender/editors/io/io_usd.c17
-rw-r--r--source/blender/editors/object/object_add.cc86
-rw-r--r--source/blender/editors/object/object_constraint.c4
-rw-r--r--source/blender/editors/object/object_edit.c24
-rw-r--r--source/blender/editors/object/object_modes.c7
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt2
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_add.cc194
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_brush.cc (renamed from source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc)33
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_comb.cc50
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_delete.cc136
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc169
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_intern.hh4
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_ops.cc20
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc44
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c197
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.cc8
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c2
-rw-r--r--source/blender/editors/space_clip/clip_buttons.c6
-rw-r--r--source/blender/editors/space_clip/clip_intern.h6
-rw-r--r--source/blender/editors/space_clip/space_clip.c167
-rw-r--r--source/blender/editors/space_clip/tracking_ops.c242
-rw-r--r--source/blender/editors/space_clip/tracking_select.c9
-rw-r--r--source/blender/editors/space_file/filesel.c18
-rw-r--r--source/blender/editors/space_image/image_ops.c27
-rw-r--r--source/blender/editors/space_nla/nla_buttons.c1
-rw-r--r--source/blender/editors/space_nla/nla_channels.c116
-rw-r--r--source/blender/editors/space_node/drawnode.cc40
-rw-r--r--source/blender/editors/space_node/node_context_path.cc2
-rw-r--r--source/blender/editors/space_node/node_draw.cc12
-rw-r--r--source/blender/editors/space_node/node_edit.cc179
-rw-r--r--source/blender/editors/space_node/node_intern.hh14
-rw-r--r--source/blender/editors/space_node/space_node.cc8
-rw-r--r--source/blender/editors/space_outliner/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.cc6
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.cc193
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.hh8
-rw-r--r--source/blender/editors/space_outliner/outliner_ops.cc1
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.cc37
-rw-r--r--source/blender/editors/space_sequencer/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc20
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc4
-rw-r--r--source/blender/editors/space_text/text_draw.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c1
-rw-r--r--source/blender/editors/transform/transform_convert.c87
-rw-r--r--source/blender/editors/transform/transform_convert_node.c4
-rw-r--r--source/blender/editors/transform/transform_convert_tracking.c10
-rw-r--r--source/blender/editors/transform/transform_snap.c58
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c33
-rw-r--r--source/blender/freestyle/intern/geometry/SweepLine.h2
-rw-r--r--source/blender/geometry/CMakeLists.txt4
-rw-r--r--source/blender/geometry/GEO_mesh_primitive_cuboid.hh18
-rw-r--r--source/blender/geometry/GEO_resample_curves.hh39
-rw-r--r--source/blender/geometry/intern/mesh_primitive_cuboid.cc423
-rw-r--r--source/blender/geometry/intern/realize_instances.cc18
-rw-r--r--source/blender/geometry/intern/resample_curves.cc474
-rw-r--r--source/blender/gpencil_modifiers/CMakeLists.txt16
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c17
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h12
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc25
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c926
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h10
-rw-r--r--source/blender/gpu/CMakeLists.txt21
-rw-r--r--source/blender/gpu/GPU_shader.h10
-rw-r--r--source/blender/gpu/GPU_texture.h4
-rw-r--r--source/blender/gpu/GPU_vertex_format.h2
-rw-r--r--source/blender/gpu/intern/gpu_capabilities.cc1
-rw-r--r--source/blender/gpu/intern/gpu_debug.cc6
-rw-r--r--source/blender/gpu/intern/gpu_immediate.cc2
-rw-r--r--source/blender/gpu/intern/gpu_shader_builtin.c15
-rw-r--r--source/blender/gpu/intern/gpu_shader_dependency.cc4
-rw-r--r--source/blender/gpu/intern/gpu_vertex_buffer.cc10
-rw-r--r--source/blender/gpu/intern/gpu_vertex_format.cc22
-rw-r--r--source/blender/gpu/metal/mtl_texture.hh2
-rw-r--r--source/blender/gpu/metal/mtl_texture.mm6
-rw-r--r--source/blender/gpu/opengl/gl_texture.hh2
-rw-r--r--source/blender/gpu/opengl/gl_vertex_array.cc4
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_color_ramp.glsl (renamed from source/blender/gpu/shaders/material/gpu_shader_material_color_ramp.glsl)0
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl (renamed from source/blender/gpu/shaders/material/gpu_shader_material_color_util.glsl)50
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl162
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_hash.glsl (renamed from source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl)0
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_math.glsl (renamed from source/blender/gpu/shaders/material/gpu_shader_material_math.glsl)2
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl (renamed from source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl)72
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl (renamed from source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl)2
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh20
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_combine_color.glsl16
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl33
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_rgb_curves.glsl73
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_separate_color.glsl28
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl4
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl4
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_transform_utils.glsl71
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl41
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl2
-rw-r--r--source/blender/gpu/tests/gpu_shader_builtin_test.cc1
-rw-r--r--source/blender/imbuf/IMB_imbuf.h8
-rw-r--r--source/blender/imbuf/intern/IMB_filetype.h17
-rw-r--r--source/blender/imbuf/intern/anim_movie.c2
-rw-r--r--source/blender/imbuf/intern/filetype.c16
-rw-r--r--source/blender/imbuf/intern/jpeg.c117
-rw-r--r--source/blender/imbuf/intern/readimage.c57
-rw-r--r--source/blender/imbuf/intern/thumbs.c55
-rw-r--r--source/blender/io/alembic/exporter/abc_export_capi.cc2
-rw-r--r--source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc5
-rw-r--r--source/blender/io/alembic/exporter/abc_hierarchy_iterator.h4
-rw-r--r--source/blender/io/common/CMakeLists.txt4
-rw-r--r--source/blender/io/common/IO_abstract_hierarchy_iterator.h4
-rw-r--r--source/blender/io/common/IO_path_util.hh6
-rw-r--r--source/blender/io/common/IO_string_utils.hh69
-rw-r--r--source/blender/io/common/intern/abstract_hierarchy_iterator.cc4
-rw-r--r--source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc5
-rw-r--r--source/blender/io/common/intern/path_util.cc3
-rw-r--r--source/blender/io/usd/CMakeLists.txt13
-rw-r--r--source/blender/io/usd/intern/usd_capi_export.cc2
-rw-r--r--source/blender/io/usd/intern/usd_exporter_context.h2
-rw-r--r--source/blender/io/usd/intern/usd_hierarchy_iterator.cc21
-rw-r--r--source/blender/io/usd/intern/usd_hierarchy_iterator.h5
-rw-r--r--source/blender/io/usd/intern/usd_writer_abstract.cc5
-rw-r--r--source/blender/io/usd/intern/usd_writer_abstract.h1
-rw-r--r--source/blender/io/usd/intern/usd_writer_material.cc10
-rw-r--r--source/blender/io/usd/intern/usd_writer_volume.cc188
-rw-r--r--source/blender/io/usd/intern/usd_writer_volume.h36
-rw-r--r--source/blender/io/usd/usd.h2
-rw-r--r--source/blender/io/wavefront_obj/CMakeLists.txt5
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc301
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_mtl.cc3
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc (renamed from source/blender/io/common/intern/string_utils.cc)57
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh82
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc (renamed from source/blender/io/common/intern/string_utils_test.cc)31
-rw-r--r--source/blender/makesdna/DNA_anim_types.h6
-rw-r--r--source/blender/makesdna/DNA_brush_enums.h7
-rw-r--r--source/blender/makesdna/DNA_brush_types.h2
-rw-r--r--source/blender/makesdna/DNA_curves_types.h25
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h8
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h6
-rw-r--r--source/blender/makesdna/DNA_image_types.h5
-rw-r--r--source/blender/makesdna/DNA_layer_types.h2
-rw-r--r--source/blender/makesdna/DNA_lineart_types.h15
-rw-r--r--source/blender/makesdna/DNA_modifier_defaults.h3
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h10
-rw-r--r--source/blender/makesdna/DNA_node_types.h27
-rw-r--r--source/blender/makesdna/DNA_object_types.h3
-rw-r--r--source/blender/makesdna/DNA_space_types.h12
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h5
-rw-r--r--source/blender/makesdna/intern/dna_rename_defs.h4
-rw-r--r--source/blender/makesrna/RNA_enum_items.h2
-rw-r--r--source/blender/makesrna/intern/rna_attribute.c16
-rw-r--r--source/blender/makesrna/intern/rna_brush.c34
-rw-r--r--source/blender/makesrna/intern/rna_curves.c26
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c20
-rw-r--r--source/blender/makesrna/intern/rna_image.c10
-rw-r--r--source/blender/makesrna/intern/rna_image_api.c5
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c4
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c77
-rw-r--r--source/blender/makesrna/intern/rna_object_force.c92
-rw-r--r--source/blender/makesrna/intern/rna_space.c13
-rw-r--r--source/blender/makesrna/intern/rna_tracking.c4
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c19
-rw-r--r--source/blender/makesrna/intern/rna_wm.c6
-rw-r--r--source/blender/makesrna/intern/rna_xr.c57
-rw-r--r--source/blender/modifiers/CMakeLists.txt2
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc9
-rw-r--r--source/blender/modifiers/intern/MOD_particlesystem.cc (renamed from source/blender/modifiers/intern/MOD_particlesystem.c)82
-rw-r--r--source/blender/modifiers/intern/MOD_screw.c125
-rw-r--r--source/blender/modifiers/intern/MOD_surfacedeform.c74
-rw-r--r--source/blender/modifiers/intern/MOD_util.h8
-rw-r--r--source/blender/nodes/NOD_composite.h2
-rw-r--r--source/blender/nodes/NOD_function.h2
-rw-r--r--source/blender/nodes/NOD_geometry_nodes_eval_log.hh8
-rw-r--r--source/blender/nodes/NOD_shader.h2
-rw-r--r--source/blender/nodes/NOD_static_types.h38
-rw-r--r--source/blender/nodes/NOD_texture.h2
-rw-r--r--source/blender/nodes/composite/CMakeLists.txt1
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc139
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc4
-rw-r--r--source/blender/nodes/function/CMakeLists.txt2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_combine_color.cc108
-rw-r--r--source/blender/nodes/function/nodes/node_fn_separate_color.cc205
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc2
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc17
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc86
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc600
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc75
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc40
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc33
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc62
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc32
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_split.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc3
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc73
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc419
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc46
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_raycast.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_id.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_position.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc98
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_triangulate.cc4
-rw-r--r--source/blender/nodes/intern/geometry_nodes_eval_log.cc12
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc18
-rw-r--r--source/blender/nodes/intern/node_util.c33
-rw-r--r--source/blender/nodes/intern/node_util.h1
-rw-r--r--source/blender/nodes/shader/CMakeLists.txt1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_curves.cc211
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcomb_color.cc162
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc4
-rw-r--r--source/blender/nodes/texture/CMakeLists.txt2
-rw-r--r--source/blender/nodes/texture/node_texture_util.c4
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_combine_color.c76
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_compose.c2
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_decompose.c2
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_separate_color.c102
-rw-r--r--source/blender/python/bmesh/bmesh_py_types.c37
-rw-r--r--source/blender/python/generic/blf_py_api.c2
-rw-r--r--source/blender/python/gpu/CMakeLists.txt2
-rw-r--r--source/blender/python/gpu/gpu_py_shader.c4
-rw-r--r--source/blender/render/intern/multires_bake.c241
-rw-r--r--source/blender/windowmanager/WM_types.h9
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c203
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_session.c4
816 files changed, 20635 insertions, 11210 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/bpy.types.Image.py b/doc/python_api/examples/bpy.types.Image.py
index 715623f6f76..f0c1a780e24 100644
--- a/doc/python_api/examples/bpy.types.Image.py
+++ b/doc/python_api/examples/bpy.types.Image.py
@@ -44,4 +44,3 @@ image_dest = image_src.copy()
image_dest.update()
print(image_dest.size)
print(image_dest.pixels[0:4])
-
diff --git a/doc/python_api/examples/gpu.6.py b/doc/python_api/examples/gpu.6.py
index be164a03028..5576b2d0bfe 100644
--- a/doc/python_api/examples/gpu.6.py
+++ b/doc/python_api/examples/gpu.6.py
@@ -29,3 +29,36 @@ def draw():
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')
+
+"""
+3D Image
+--------
+
+Similar to the 2D Image shader, but works with 3D positions for the image vertices.
+To use this example you have to provide an image that should be displayed.
+"""
+import bpy
+import gpu
+from gpu_extras.batch import batch_for_shader
+
+IMAGE_NAME = "Untitled"
+image = bpy.data.images[IMAGE_NAME]
+texture = gpu.texture.from_image(image)
+
+shader = gpu.shader.from_builtin('3D_IMAGE')
+batch = batch_for_shader(
+ shader, 'TRIS',
+ {
+ "pos": ((0, 0, 0), (0, 1, 1), (1, 1, 1), (1, 1, 1), (1, 0, 0), (0, 0, 0)),
+ "texCoord": ((0, 0), (0, 1), (1, 1), (1, 1), (1, 0), (0, 0)),
+ },
+)
+
+
+def draw():
+ shader.bind()
+ shader.uniform_sampler("image", texture)
+ batch.draw(shader)
+
+
+bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
diff --git a/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_, &parameters, 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,
+ &parameters_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(&parameters, residuals, NULL);
+ return cost_function_.Evaluate(&parameters, 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 &parameter_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/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..fc9a8cecd75 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 specialised 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 specialisations 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..27d7f7fb8e1 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 &params)
+{
+ 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 &params,
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 &params,
const array<int> &tiles);
ImageHandle add_image(ImageLoader *loader, const ImageParams &params, const bool builtin = true);
+ ImageHandle add_image(const vector<ImageLoader *> &loaders, const ImageParams &params);
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_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 83869188b65..8e07bf4ea3d 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -8,12 +8,14 @@
#include "GHOST_SystemWin32.h"
#include "GHOST_ContextD3D.h"
#include "GHOST_EventDragnDrop.h"
+#include "GHOST_EventTrackpad.h"
#ifndef _WIN32_IE
# define _WIN32_IE 0x0501 /* shipped before XP, so doesn't impose additional requirements */
#endif
#include <commctrl.h>
+#include <dwmapi.h>
#include <psapi.h>
#include <shellapi.h>
#include <shellscalingapi.h>
@@ -414,6 +416,8 @@ bool GHOST_SystemWin32::processEvents(bool waitForEvent)
hasEventHandled = true;
}
+ driveTrackpad();
+
// Process all the events waiting for us
while (::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) {
// TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data.
@@ -423,6 +427,8 @@ bool GHOST_SystemWin32::processEvents(bool waitForEvent)
hasEventHandled = true;
}
+ processTrackpad();
+
/* PeekMessage above is allowed to dispatch messages to the wndproc without us
* noticing, so we need to check the event manager here to see if there are
* events waiting in the queue.
@@ -1416,6 +1422,52 @@ bool GHOST_SystemWin32::processNDOF(RAWINPUT const &raw)
}
#endif // WITH_INPUT_NDOF
+void GHOST_SystemWin32::driveTrackpad()
+{
+ GHOST_WindowWin32 *active_window = static_cast<GHOST_WindowWin32 *>(
+ getWindowManager()->getActiveWindow());
+ if (active_window) {
+ active_window->updateDirectManipulation();
+ }
+}
+
+void GHOST_SystemWin32::processTrackpad()
+{
+ GHOST_WindowWin32 *active_window = static_cast<GHOST_WindowWin32 *>(
+ getWindowManager()->getActiveWindow());
+
+ if (!active_window) {
+ return;
+ }
+
+ GHOST_TTrackpadInfo trackpad_info = active_window->getTrackpadInfo();
+ GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
+
+ int32_t cursor_x, cursor_y;
+ system->getCursorPosition(cursor_x, cursor_y);
+
+ if (trackpad_info.x != 0 || trackpad_info.y != 0) {
+ system->pushEvent(new GHOST_EventTrackpad(system->getMilliSeconds(),
+ active_window,
+ GHOST_kTrackpadEventScroll,
+ cursor_x,
+ cursor_y,
+ trackpad_info.x,
+ trackpad_info.y,
+ trackpad_info.isScrollDirectionInverted));
+ }
+ if (trackpad_info.scale != 0) {
+ system->pushEvent(new GHOST_EventTrackpad(system->getMilliSeconds(),
+ active_window,
+ GHOST_kTrackpadEventMagnify,
+ cursor_x,
+ cursor_y,
+ trackpad_info.scale,
+ 0,
+ false));
+ }
+}
+
LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
GHOST_Event *event = NULL;
@@ -1968,6 +2020,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
suggestedWindowRect->right - suggestedWindowRect->left,
suggestedWindowRect->bottom - suggestedWindowRect->top,
SWP_NOZORDER | SWP_NOACTIVATE);
+
+ window->updateDPI();
}
break;
case WM_DISPLAYCHANGE: {
@@ -1985,6 +2039,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
::SetFocus(hwnd);
}
break;
+ case WM_SETTINGCHANGE:
+ /* Microsoft: "Note that some applications send this message with lParam set to NULL" */
+ if ((lParam != NULL) && (wcscmp(LPCWSTR(lParam), L"ImmersiveColorSet") == 0)) {
+ window->ThemeRefresh();
+ }
+ break;
////////////////////////////////////////////////////////////////////////
// Window events, ignored
////////////////////////////////////////////////////////////////////////
@@ -2056,6 +2116,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
* In GHOST, we let DefWindowProc call the timer callback.
*/
break;
+ case DM_POINTERHITTEST:
+ /* The DM_POINTERHITTEST message is sent to a window, when pointer input is first
+ * detected, in order to determine the most probable input target for Direct
+ * Manipulation. */
+ window->onPointerHitTest(wParam);
+ break;
}
}
else {
diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h
index 9f8d52f9ca3..689b78b0317 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.h
+++ b/intern/ghost/intern/GHOST_SystemWin32.h
@@ -407,6 +407,16 @@ class GHOST_SystemWin32 : public GHOST_System {
#endif
/**
+ * Drives Direct Manipulation update.
+ */
+ void driveTrackpad();
+
+ /**
+ * Creates trackpad events for the active window.
+ */
+ void processTrackpad();
+
+ /**
* Returns the local state of the modifier keys (from the message queue).
* \param keys: The state of the keys.
*/
diff --git a/intern/ghost/intern/GHOST_TrackpadWin32.cpp b/intern/ghost/intern/GHOST_TrackpadWin32.cpp
new file mode 100644
index 00000000000..d5317f0f780
--- /dev/null
+++ b/intern/ghost/intern/GHOST_TrackpadWin32.cpp
@@ -0,0 +1,343 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+#include <cmath>
+
+#include "GHOST_Debug.h"
+#include "GHOST_TrackpadWin32.h"
+
+GHOST_DirectManipulationHelper::GHOST_DirectManipulationHelper(
+ HWND hWnd,
+ Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager,
+ Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager,
+ Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport,
+ Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
+ directManipulationEventHandler,
+ DWORD directManipulationViewportHandlerCookie,
+ bool isScrollDirectionInverted)
+ : m_hWnd(hWnd),
+ m_scrollDirectionRegKey(NULL),
+ m_scrollDirectionChangeEvent(NULL),
+ m_directManipulationManager(directManipulationManager),
+ m_directManipulationUpdateManager(directManipulationUpdateManager),
+ m_directManipulationViewport(directManipulationViewport),
+ m_directManipulationEventHandler(directManipulationEventHandler),
+ m_directManipulationViewportHandlerCookie(directManipulationViewportHandlerCookie),
+ m_isScrollDirectionInverted(isScrollDirectionInverted)
+{
+}
+
+GHOST_DirectManipulationHelper *GHOST_DirectManipulationHelper::create(HWND hWnd, uint16_t dpi)
+{
+#define DM_CHECK_RESULT_AND_EXIT_EARLY(hr, failMessage) \
+ { \
+ if (!SUCCEEDED(hr)) { \
+ GHOST_PRINT(failMessage); \
+ return nullptr; \
+ } \
+ }
+
+ Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager;
+ HRESULT hr = ::CoCreateInstance(CLSID_DirectManipulationManager,
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_PPV_ARGS(&directManipulationManager));
+ DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "DirectManipulationManager create failed\n");
+
+ /* Since we want to use fake viewport, we need to send fake updates to UpdateManager. */
+ Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager;
+ hr = directManipulationManager->GetUpdateManager(IID_PPV_ARGS(&directManipulationUpdateManager));
+ DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Get UpdateManager failed\n");
+
+ Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport;
+ hr = directManipulationManager->CreateViewport(
+ nullptr, hWnd, IID_PPV_ARGS(&directManipulationViewport));
+ DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport create failed\n");
+
+ DIRECTMANIPULATION_CONFIGURATION configuration =
+ DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
+ DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X |
+ DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y |
+ DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA |
+ DIRECTMANIPULATION_CONFIGURATION_SCALING;
+
+ hr = directManipulationViewport->ActivateConfiguration(configuration);
+ DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set ActivateConfiguration failed\n");
+
+ /* Since we are using fake viewport and only want to use Direct Manipulation for touchpad, we
+ * need to use MANUALUPDATE option. */
+ hr = directManipulationViewport->SetViewportOptions(
+ DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE);
+ DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set ViewportOptions failed\n");
+
+ /* We receive Direct Manipulation transform updates in IDirectManipulationViewportEventHandler
+ * callbacks. */
+ Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
+ directManipulationEventHandler =
+ Microsoft::WRL::Make<GHOST_DirectManipulationViewportEventHandler>(dpi);
+ DWORD directManipulationViewportHandlerCookie;
+ directManipulationViewport->AddEventHandler(
+ hWnd, directManipulationEventHandler.Get(), &directManipulationViewportHandlerCookie);
+ DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport add EventHandler failed\n");
+
+ /* Set default rect for viewport before activating. */
+ RECT rect = {0, 0, 10000, 10000};
+ hr = directManipulationViewport->SetViewportRect(&rect);
+ DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set rect failed\n");
+
+ hr = directManipulationManager->Activate(hWnd);
+ DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "DirectManipulationManager activate failed\n");
+
+ hr = directManipulationViewport->Enable();
+ DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport enable failed\n");
+
+ directManipulationEventHandler->resetViewport(directManipulationViewport.Get());
+
+ bool isScrollDirectionInverted = getScrollDirectionFromReg();
+
+ auto instance = new GHOST_DirectManipulationHelper(hWnd,
+ directManipulationManager,
+ directManipulationUpdateManager,
+ directManipulationViewport,
+ directManipulationEventHandler,
+ directManipulationViewportHandlerCookie,
+ isScrollDirectionInverted);
+
+ instance->registerScrollDirectionChangeListener();
+
+ return instance;
+
+#undef DM_CHECK_RESULT_AND_EXIT_EARLY
+}
+
+bool GHOST_DirectManipulationHelper::getScrollDirectionFromReg()
+{
+ DWORD scrollDirectionRegValue, pcbData;
+ HRESULT hr = HRESULT_FROM_WIN32(
+ RegGetValueW(HKEY_CURRENT_USER,
+ L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PrecisionTouchPad\\",
+ L"ScrollDirection",
+ RRF_RT_REG_DWORD,
+ NULL,
+ &scrollDirectionRegValue,
+ &pcbData));
+ if (!SUCCEEDED(hr)) {
+ GHOST_PRINT("Failed to get scroll direction from registry\n");
+ return false;
+ }
+
+ return scrollDirectionRegValue == 0;
+}
+
+void GHOST_DirectManipulationHelper::registerScrollDirectionChangeListener()
+{
+
+ if (!m_scrollDirectionRegKey) {
+ HRESULT hr = HRESULT_FROM_WIN32(
+ RegOpenKeyExW(HKEY_CURRENT_USER,
+ L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PrecisionTouchPad\\",
+ 0,
+ KEY_NOTIFY,
+ &m_scrollDirectionRegKey));
+ if (!SUCCEEDED(hr)) {
+ GHOST_PRINT("Failed to open scroll direction registry key\n");
+ return;
+ }
+ }
+
+ if (!m_scrollDirectionChangeEvent) {
+ m_scrollDirectionChangeEvent = CreateEventW(NULL, true, false, NULL);
+ }
+ else {
+ ResetEvent(m_scrollDirectionChangeEvent);
+ }
+ HRESULT hr = HRESULT_FROM_WIN32(RegNotifyChangeKeyValue(m_scrollDirectionRegKey,
+ true,
+ REG_NOTIFY_CHANGE_LAST_SET,
+ m_scrollDirectionChangeEvent,
+ true));
+ if (!SUCCEEDED(hr)) {
+ GHOST_PRINT("Failed to register scroll direction change listener\n");
+ return;
+ }
+}
+
+void GHOST_DirectManipulationHelper::onPointerHitTest(UINT32 pointerId)
+{
+ [[maybe_unused]] HRESULT hr = m_directManipulationViewport->SetContact(pointerId);
+ GHOST_ASSERT(SUCCEEDED(hr), "Viewport set contact failed\n");
+
+ if (WaitForSingleObject(m_scrollDirectionChangeEvent, 0) == WAIT_OBJECT_0) {
+ m_isScrollDirectionInverted = getScrollDirectionFromReg();
+ registerScrollDirectionChangeListener();
+ }
+}
+
+void GHOST_DirectManipulationHelper::update()
+{
+ if (m_directManipulationEventHandler->dm_status == DIRECTMANIPULATION_RUNNING ||
+ m_directManipulationEventHandler->dm_status == DIRECTMANIPULATION_INERTIA) {
+ [[maybe_unused]] HRESULT hr = m_directManipulationUpdateManager->Update(nullptr);
+ GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationUpdateManager update failed\n");
+ }
+}
+
+void GHOST_DirectManipulationHelper::setDPI(uint16_t dpi)
+{
+ m_directManipulationEventHandler->dpi = dpi;
+}
+
+GHOST_TTrackpadInfo GHOST_DirectManipulationHelper::getTrackpadInfo()
+{
+ GHOST_TTrackpadInfo result = m_directManipulationEventHandler->accumulated_values;
+ result.isScrollDirectionInverted = m_isScrollDirectionInverted;
+
+ m_directManipulationEventHandler->accumulated_values = {0, 0, 0};
+ return result;
+}
+
+GHOST_DirectManipulationHelper::~GHOST_DirectManipulationHelper()
+{
+ HRESULT hr;
+ hr = m_directManipulationViewport->Stop();
+ GHOST_ASSERT(SUCCEEDED(hr), "Viewport stop failed\n");
+
+ hr = m_directManipulationViewport->RemoveEventHandler(m_directManipulationViewportHandlerCookie);
+ GHOST_ASSERT(SUCCEEDED(hr), "Viewport remove event handler failed\n");
+
+ hr = m_directManipulationViewport->Abandon();
+ GHOST_ASSERT(SUCCEEDED(hr), "Viewport abandon failed\n");
+
+ hr = m_directManipulationManager->Deactivate(m_hWnd);
+ GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationManager deactivate failed\n");
+
+ if (m_scrollDirectionChangeEvent) {
+ CloseHandle(m_scrollDirectionChangeEvent);
+ m_scrollDirectionChangeEvent = NULL;
+ }
+ if (m_scrollDirectionRegKey) {
+ RegCloseKey(m_scrollDirectionRegKey);
+ m_scrollDirectionRegKey = NULL;
+ }
+}
+
+GHOST_DirectManipulationViewportEventHandler::GHOST_DirectManipulationViewportEventHandler(
+ uint16_t dpi)
+ : accumulated_values({0, 0, 0}), dpi(dpi), dm_status(DIRECTMANIPULATION_BUILDING)
+{
+}
+
+void GHOST_DirectManipulationViewportEventHandler::resetViewport(
+ IDirectManipulationViewport *viewport)
+{
+ if (gesture_state != GESTURE_NONE) {
+ [[maybe_unused]] HRESULT hr = viewport->ZoomToRect(0.0f, 0.0f, 10000.0f, 10000.0f, FALSE);
+ GHOST_ASSERT(SUCCEEDED(hr), "Viewport reset failed\n");
+ }
+
+ gesture_state = GESTURE_NONE;
+
+ last_scale = PINCH_SCALE_FACTOR;
+ last_x = 0.0f;
+ last_y = 0.0f;
+}
+
+HRESULT GHOST_DirectManipulationViewportEventHandler::OnViewportStatusChanged(
+ IDirectManipulationViewport *viewport,
+ DIRECTMANIPULATION_STATUS current,
+ DIRECTMANIPULATION_STATUS previous)
+{
+ dm_status = current;
+
+ if (current == previous) {
+ return S_OK;
+ }
+
+ if (previous == DIRECTMANIPULATION_ENABLED || current == DIRECTMANIPULATION_READY ||
+ (previous == DIRECTMANIPULATION_INERTIA && current != DIRECTMANIPULATION_INERTIA)) {
+ resetViewport(viewport);
+ }
+
+ return S_OK;
+}
+
+HRESULT GHOST_DirectManipulationViewportEventHandler::OnViewportUpdated(
+ IDirectManipulationViewport *viewport)
+{
+ /* Nothing to do here. */
+ return S_OK;
+}
+
+HRESULT GHOST_DirectManipulationViewportEventHandler::OnContentUpdated(
+ IDirectManipulationViewport *viewport, IDirectManipulationContent *content)
+{
+ float transform[6];
+ HRESULT hr = content->GetContentTransform(transform, ARRAYSIZE(transform));
+ GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationContent get transform failed\n");
+
+ const float device_scale_factor = dpi / 96.0f;
+
+ const float scale = transform[0] * PINCH_SCALE_FACTOR;
+ const float x = transform[4] / device_scale_factor;
+ const float y = transform[5] / device_scale_factor;
+
+ const float EPS = 3e-5;
+
+ /* Ignore repeating or incorrect input. */
+ if ((fabs(scale - last_scale) <= EPS && fabs(x - last_x) <= EPS && fabs(y - last_y) <= EPS) ||
+ scale == 0.0f) {
+ GHOST_PRINT("Ignoring touchpad input\n");
+ return hr;
+ }
+
+ /* Assume that every gesture is a pan in the beginning.
+ * If it's a pinch, the gesture will be changed below. */
+ if (gesture_state == GESTURE_NONE) {
+ gesture_state = GESTURE_PAN;
+ }
+
+ /* DM doesn't always immediately recognize pinch gestures,
+ * so allow transition from pan to pinch. */
+ if (gesture_state == GESTURE_PAN) {
+ if (fabs(scale - PINCH_SCALE_FACTOR) > EPS) {
+ gesture_state = GESTURE_PINCH;
+ }
+ }
+
+ /* This state machine is used here because:
+ * 1. Pinch and pan gestures must be differentiated and cannot be processed at the same time
+ * because XY transform values become nonsensical during pinch gesture.
+ * 2. GHOST requires delta values for events while DM provides transformation matrix of the
+ * current gesture.
+ * 3. GHOST events accept integer values while DM values are non-integer.
+ * Truncated fractional parts are accumulated and accounted for in following updates.
+ */
+ switch (gesture_state) {
+ case GESTURE_PINCH: {
+ int32_t dscale = roundf(scale - last_scale);
+
+ last_scale += dscale;
+
+ accumulated_values.scale += dscale;
+ break;
+ }
+ case GESTURE_PAN: {
+ int32_t dx = roundf(x - last_x);
+ int32_t dy = roundf(y - last_y);
+
+ last_x += dx;
+ last_y += dy;
+
+ accumulated_values.x += dx;
+ accumulated_values.y += dy;
+ break;
+ }
+ case GESTURE_NONE:
+ break;
+ }
+
+ return hr;
+}
diff --git a/intern/ghost/intern/GHOST_TrackpadWin32.h b/intern/ghost/intern/GHOST_TrackpadWin32.h
new file mode 100644
index 00000000000..2e28f756965
--- /dev/null
+++ b/intern/ghost/intern/GHOST_TrackpadWin32.h
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup GHOST
+ * Declaration of GHOST DirectManipulation classes.
+ */
+
+#pragma once
+
+#ifndef WIN32
+# error WIN32 only!
+#endif // WIN32
+
+#include "GHOST_Types.h"
+
+#include <directmanipulation.h>
+#include <wrl.h>
+
+#define PINCH_SCALE_FACTOR 125.0f
+
+typedef struct {
+ int32_t x, y, scale;
+ bool isScrollDirectionInverted;
+} GHOST_TTrackpadInfo;
+
+class GHOST_DirectManipulationHelper;
+
+class GHOST_DirectManipulationViewportEventHandler
+ : public Microsoft::WRL::RuntimeClass<
+ Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>,
+ Microsoft::WRL::Implements<
+ Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>,
+ Microsoft::WRL::FtmBase,
+ IDirectManipulationViewportEventHandler>> {
+ public:
+ GHOST_DirectManipulationViewportEventHandler(uint16_t dpi);
+
+ /*
+ * Resets viewport and tracked touchpad state.
+ */
+ void resetViewport(IDirectManipulationViewport *viewport);
+
+ /* DirectManipulation callbacks. */
+ HRESULT STDMETHODCALLTYPE OnViewportStatusChanged(IDirectManipulationViewport *viewport,
+ DIRECTMANIPULATION_STATUS current,
+ DIRECTMANIPULATION_STATUS previous) override;
+
+ HRESULT STDMETHODCALLTYPE OnViewportUpdated(IDirectManipulationViewport *viewport) override;
+
+ HRESULT STDMETHODCALLTYPE OnContentUpdated(IDirectManipulationViewport *viewport,
+ IDirectManipulationContent *content) override;
+
+ private:
+ enum { GESTURE_NONE, GESTURE_PAN, GESTURE_PINCH } gesture_state;
+
+ int32_t last_x, last_y, last_scale;
+ GHOST_TTrackpadInfo accumulated_values;
+ uint16_t dpi;
+ DIRECTMANIPULATION_STATUS dm_status;
+
+ friend class GHOST_DirectManipulationHelper;
+};
+
+class GHOST_DirectManipulationHelper {
+ public:
+ /*
+ * Creates a GHOST_DirectManipulationHelper for the provided window.
+ * \param hWnd: The window receiving DirectManipulation events.
+ * \param dpi: The current DPI.
+ * \return Pointer to the new GHOST_DirectManipulationHelper if created, nullptr if there was an
+ * error.
+ */
+ static GHOST_DirectManipulationHelper *create(HWND hWnd, uint16_t dpi);
+
+ ~GHOST_DirectManipulationHelper();
+
+ /*
+ * Drives the DirectManipulation context.
+ * DirectManipulation's intended use is to tie user input into DirectComposition's compositor
+ * scaling and translating. We are not using DirectComposition and therefore must drive
+ * DirectManipulation manually.
+ */
+ void update();
+
+ /*
+ * Sets pointer in contact with the DirectManipulation context.
+ * \param pointerId: ID of the pointer in contact.
+ */
+ void onPointerHitTest(UINT32 pointerId);
+
+ /*
+ * Updates DPI information for touchpad scaling.
+ * \param dpi: The new DPI.
+ */
+ void setDPI(uint16_t dpi);
+
+ /*
+ * Retrieves trackpad input.
+ * \return The accumulated trackpad translation and scale since last call.
+ */
+ GHOST_TTrackpadInfo getTrackpadInfo();
+
+ private:
+ GHOST_DirectManipulationHelper(
+ HWND hWnd,
+ Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager,
+ Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager,
+ Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport,
+ Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
+ directManipulationEventHandler,
+ DWORD directManipulationViewportHandlerCookie,
+ bool isScrollDirectionInverted);
+
+ /*
+ * Retrieves the scroll direction from the registry.
+ * \return True if scroll direction is inverted.
+ */
+ static bool getScrollDirectionFromReg();
+
+ /*
+ * Registers listener for registry scroll direction entry changes.
+ */
+ void registerScrollDirectionChangeListener();
+
+ HWND m_hWnd;
+
+ HKEY m_scrollDirectionRegKey;
+ HANDLE m_scrollDirectionChangeEvent;
+
+ Microsoft::WRL::ComPtr<IDirectManipulationManager> m_directManipulationManager;
+ Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> m_directManipulationUpdateManager;
+ Microsoft::WRL::ComPtr<IDirectManipulationViewport> m_directManipulationViewport;
+ Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
+ m_directManipulationEventHandler;
+ DWORD m_directManipulationViewportHandlerCookie;
+
+ bool m_isScrollDirectionInverted;
+};
diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp
index 2ce224b666b..897e6c145da 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.cpp
+++ b/intern/ghost/intern/GHOST_WindowWin32.cpp
@@ -16,9 +16,7 @@
#include "GHOST_ContextWGL.h"
-#ifdef WIN32_COMPOSITING
-# include <Dwmapi.h>
-#endif
+#include <Dwmapi.h>
#include <assert.h>
#include <math.h>
@@ -70,6 +68,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
m_normal_state(GHOST_kWindowStateNormal),
m_user32(::LoadLibrary("user32.dll")),
m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP),
+ m_directManipulationHelper(NULL),
m_debug_context(is_debug)
{
DWORD style = parentwindow ?
@@ -172,6 +171,8 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
break;
}
+ ThemeRefresh();
+
::ShowWindow(m_hWnd, nCmdShow);
#ifdef WIN32_COMPOSITING
@@ -204,6 +205,42 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
/* Allow the showing of a progress bar on the taskbar. */
CoCreateInstance(
CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar);
+
+ /* Initialize Direct Manipulation. */
+ m_directManipulationHelper = GHOST_DirectManipulationHelper::create(m_hWnd, getDPIHint());
+}
+
+void GHOST_WindowWin32::updateDirectManipulation()
+{
+ if (!m_directManipulationHelper) {
+ return;
+ }
+
+ m_directManipulationHelper->update();
+}
+
+void GHOST_WindowWin32::onPointerHitTest(WPARAM wParam)
+{
+ /* Only DM_POINTERHITTEST can be the first message of input sequence of touchpad input. */
+
+ if (!m_directManipulationHelper) {
+ return;
+ }
+
+ UINT32 pointerId = GET_POINTERID_WPARAM(wParam);
+ POINTER_INPUT_TYPE pointerType;
+ if (GetPointerType(pointerId, &pointerType) && pointerType == PT_TOUCHPAD) {
+ m_directManipulationHelper->onPointerHitTest(pointerId);
+ }
+}
+
+GHOST_TTrackpadInfo GHOST_WindowWin32::getTrackpadInfo()
+{
+ if (!m_directManipulationHelper) {
+ return {0, 0, 0};
+ }
+
+ return m_directManipulationHelper->getTrackpadInfo();
}
GHOST_WindowWin32::~GHOST_WindowWin32()
@@ -253,6 +290,9 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
::DestroyWindow(m_hWnd);
m_hWnd = 0;
}
+
+ delete m_directManipulationHelper;
+ m_directManipulationHelper = NULL;
}
void GHOST_WindowWin32::adjustWindowRectForClosestMonitor(LPRECT win_rect,
@@ -1016,6 +1056,32 @@ GHOST_TabletData GHOST_WindowWin32::getTabletData()
}
}
+void GHOST_WindowWin32::ThemeRefresh()
+{
+ DWORD lightMode;
+ DWORD pcbData = sizeof(lightMode);
+ if (RegGetValueW(HKEY_CURRENT_USER,
+ L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize\\",
+ L"AppsUseLightTheme",
+ RRF_RT_REG_DWORD,
+ NULL,
+ &lightMode,
+ &pcbData) == ERROR_SUCCESS) {
+ BOOL DarkMode = !lightMode;
+
+ /* 20 == DWMWA_USE_IMMERSIVE_DARK_MODE in Windows 11 SDK. This value was undocumented for
+ * Windows 10 versions 2004 and later, supported for Windows 11 Build 22000 and later. */
+ DwmSetWindowAttribute(this->m_hWnd, 20, &DarkMode, sizeof(DarkMode));
+ }
+}
+
+void GHOST_WindowWin32::updateDPI()
+{
+ if (m_directManipulationHelper) {
+ m_directManipulationHelper->setDPI(getDPIHint());
+ }
+}
+
uint16_t GHOST_WindowWin32::getDPIHint()
{
if (m_user32) {
diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h
index d5f47871aff..c958a89ac48 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.h
+++ b/intern/ghost/intern/GHOST_WindowWin32.h
@@ -13,6 +13,7 @@
#endif // WIN32
#include "GHOST_TaskbarWin32.h"
+#include "GHOST_TrackpadWin32.h"
#include "GHOST_Window.h"
#include "GHOST_Wintab.h"
#ifdef WITH_INPUT_IME
@@ -286,6 +287,8 @@ class GHOST_WindowWin32 : public GHOST_Window {
return GHOST_kFailure;
}
+ void updateDPI();
+
uint16_t getDPIHint() override;
/** True if the mouse is either over or captured by the window. */
@@ -294,6 +297,9 @@ class GHOST_WindowWin32 : public GHOST_Window {
/** True if the window currently resizing. */
bool m_inLiveResize;
+ /** Called when OS colors change and when the window is created. */
+ void ThemeRefresh();
+
#ifdef WITH_INPUT_IME
GHOST_ImeWin32 *getImeInput()
{
@@ -305,6 +311,19 @@ class GHOST_WindowWin32 : public GHOST_Window {
void endIME();
#endif /* WITH_INPUT_IME */
+ /*
+ * Drive DirectManipulation context.
+ */
+ void updateDirectManipulation();
+
+ /*
+ * Handle DM_POINTERHITTEST events.
+ * \param wParam: wParam from the event.
+ */
+ void onPointerHitTest(WPARAM wParam);
+
+ GHOST_TTrackpadInfo getTrackpadInfo();
+
private:
/**
* \param type: The type of rendering context create.
@@ -388,6 +407,8 @@ class GHOST_WindowWin32 : public GHOST_Window {
HWND m_parentWindowHwnd;
+ GHOST_DirectManipulationHelper *m_directManipulationHelper;
+
#ifdef WITH_INPUT_IME
/** Handle input method editors event */
GHOST_ImeWin32 m_imeInput;
diff --git a/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/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/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
new file mode 100644
index 00000000000..195a5b9d6d9
--- /dev/null
+++ b/release/datafiles/blender_icons16/icon16_geometry_nodes.dat
Binary files differ
diff --git a/release/datafiles/blender_icons32/icon32_geometry_nodes.dat b/release/datafiles/blender_icons32/icon32_geometry_nodes.dat
new file mode 100644
index 00000000000..50971291fcc
--- /dev/null
+++ b/release/datafiles/blender_icons32/icon32_geometry_nodes.dat
Binary files differ
diff --git a/release/datafiles/icons/ops.curves.sculpt_puff.dat b/release/datafiles/icons/ops.curves.sculpt_puff.dat
new file mode 100644
index 00000000000..db2bab46bfe
--- /dev/null
+++ b/release/datafiles/icons/ops.curves.sculpt_puff.dat
Binary files differ
diff --git a/release/datafiles/splash.png b/release/datafiles/splash.png
index 332ee39849e..eb1250cf5a5 100644
--- a/release/datafiles/splash.png
+++ b/release/datafiles/splash.png
Binary files differ
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_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..189210d8540 100644
--- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
+++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
@@ -41,17 +41,7 @@ class AnnotationDrawingToolsPanel:
row.prop_enum(tool_settings, "annotation_stroke_placement_view2d", 'IMAGE', text="Image")
-class GreasePencilSculptOptionsPanel:
- bl_label = "Sculpt Strokes"
-
- @classmethod
- def poll(cls, context):
- tool_settings = context.scene.tool_settings
- settings = tool_settings.gpencil_sculpt_paint
- brush = settings.brush
- tool = brush.gpencil_sculpt_tool
-
- return bool(tool in {'SMOOTH', 'RANDOMIZE'})
+class GreasePencilSculptAdvancedPanel:
def draw(self, context):
layout = self.layout
@@ -59,17 +49,21 @@ class GreasePencilSculptOptionsPanel:
layout.use_property_decorate = False
tool_settings = context.scene.tool_settings
- settings = tool_settings.gpencil_sculpt_paint
- brush = settings.brush
- gp_settings = brush.gpencil_settings
+ brush = context.tool_settings.gpencil_sculpt_paint.brush
tool = brush.gpencil_sculpt_tool
+ gp_settings = brush.gpencil_settings
- if tool in {'SMOOTH', 'RANDOMIZE'}:
- layout.prop(gp_settings, "use_edit_position", text="Affect Position")
- layout.prop(gp_settings, "use_edit_strength", text="Affect Strength")
- layout.prop(gp_settings, "use_edit_thickness", text="Affect Thickness")
+ col = layout.column(heading="Auto-Masking", align=True)
+ col.prop(gp_settings, "use_automasking_stroke", text="Stroke")
+ col.prop(gp_settings, "use_automasking_layer", text="Layer")
+ col.prop(gp_settings, "use_automasking_material", text="Material")
- layout.prop(gp_settings, "use_edit_uv", text="Affect UV")
+ if tool in {'SMOOTH', 'RANDOMIZE'}:
+ col = layout.column(heading="Affect", align=True)
+ col.prop(gp_settings, "use_edit_position", text="Position")
+ col.prop(gp_settings, "use_edit_strength", text="Strength")
+ col.prop(gp_settings, "use_edit_thickness", text="Thickness")
+ col.prop(gp_settings, "use_edit_uv", text="UV")
# GP Object Tool Settings
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py
index 9a116aa1717..b8136f26e90 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,27 @@ 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")
+ use_frontface = True
+ elif brush.curves_sculpt_tool == 'GROW_SHRINK':
+ layout.prop(brush.curves_sculpt_settings, "scale_uniform")
+ layout.prop(brush.curves_sculpt_settings, "minimum_length")
+
def brush_shared_settings(layout, context, brush, popover=False):
""" Draw simple brush settings that are shared between different paint modes. """
@@ -827,6 +860,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 +959,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_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index af56b966f17..d4d1e6ace76 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -2274,6 +2274,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
self._draw_items(
context, (
({"property": "use_new_curves_type"}, "T68981"),
+ ({"property": "use_new_curves_tools"}, "T68981"),
({"property": "use_new_point_cloud_type"}, "T75717"),
({"property": "use_full_frame_compositor"}, "T88150"),
({"property": "enable_eevee_next"}, "T93220"),
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index a8624030564..995b2afebf0 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")
@@ -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..3e1754fd908 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,
)
@@ -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 4f94184f006..817c53cca62 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -419,12 +419,10 @@ shader_node_categories = [
NodeItem("ShaderNodeRGBToBW"),
NodeItem("ShaderNodeShaderToRGB", poll=object_eevee_shader_nodes_poll),
NodeItem("ShaderNodeVectorMath"),
- NodeItem("ShaderNodeSeparateRGB"),
- NodeItem("ShaderNodeCombineRGB"),
+ NodeItem("ShaderNodeSeparateColor"),
+ NodeItem("ShaderNodeCombineColor"),
NodeItem("ShaderNodeSeparateXYZ"),
NodeItem("ShaderNodeCombineXYZ"),
- NodeItem("ShaderNodeSeparateHSV"),
- NodeItem("ShaderNodeCombineHSV"),
NodeItem("ShaderNodeWavelength"),
NodeItem("ShaderNodeBlackbody"),
]),
@@ -483,14 +481,8 @@ compositor_node_categories = [
NodeItem("CompositorNodePremulKey"),
NodeItem("CompositorNodeIDMask"),
NodeItem("CompositorNodeRGBToBW"),
- NodeItem("CompositorNodeSepRGBA"),
- NodeItem("CompositorNodeCombRGBA"),
- NodeItem("CompositorNodeSepHSVA"),
- NodeItem("CompositorNodeCombHSVA"),
- NodeItem("CompositorNodeSepYUVA"),
- NodeItem("CompositorNodeCombYUVA"),
- NodeItem("CompositorNodeSepYCCA"),
- NodeItem("CompositorNodeCombYCCA"),
+ NodeItem("CompositorNodeSeparateColor"),
+ NodeItem("CompositorNodeCombineColor"),
NodeItem("CompositorNodeSeparateXYZ"),
NodeItem("CompositorNodeCombineXYZ"),
NodeItem("CompositorNodeSwitchView"),
@@ -576,8 +568,8 @@ texture_node_categories = [
NodeItem("TextureNodeCurveRGB"),
NodeItem("TextureNodeInvert"),
NodeItem("TextureNodeHueSaturation"),
- NodeItem("TextureNodeCompose"),
- NodeItem("TextureNodeDecompose"),
+ NodeItem("TextureNodeCombineColor"),
+ NodeItem("TextureNodeSeparateColor"),
]),
TextureNodeCategory("TEX_PATTERN", "Pattern", items=[
NodeItem("TextureNodeChecker"),
@@ -629,8 +621,8 @@ geometry_node_categories = [
NodeItem("ShaderNodeMixRGB"),
NodeItem("ShaderNodeRGBCurve"),
NodeItem("ShaderNodeValToRGB"),
- NodeItem("ShaderNodeSeparateRGB"),
- NodeItem("ShaderNodeCombineRGB"),
+ NodeItem("FunctionNodeSeparateColor"),
+ NodeItem("FunctionNodeCombineColor"),
]),
GeometryNodeCategory("GEO_CURVE", "Curve", items=curve_node_items),
GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index ffc4d37f622..d0592e9a405 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -15,8 +15,9 @@ if(WITH_CLANG_TIDY AND NOT MSVC)
endif()
find_package(ClangTidy REQUIRED)
- set(CMAKE_C_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE})
- set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE})
+ set(CMAKE_C_CLANG_TIDY
+ ${CLANG_TIDY_EXECUTABLE};--extra-arg=-Wno-error=unknown-warning-option)
+ set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE};--extra-arg=-Wno-error=unknown-warning-option)
endif()
add_subdirectory(blender)
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index f93cb8b2d64..a170f27d247 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -1374,7 +1374,7 @@ bool blf_font_size(FontBLF *font, float size, unsigned int dpi)
font->dpi = dpi;
}
else {
- printf("The current font does not support the size, %f and dpi, %u\n", size, dpi);
+ printf("The current font does not support the size, %f and DPI, %u\n", size, dpi);
return false;
}
}
diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h
index 62bce36dda0..9dfcb1a4ad6 100644
--- a/source/blender/blenfont/intern/blf_internal_types.h
+++ b/source/blender/blenfont/intern/blf_internal_types.h
@@ -123,7 +123,7 @@ typedef struct GlyphCacheBLF {
/* font size. */
float size;
- /* and dpi. */
+ /* and DPI. */
unsigned int dpi;
bool bold;
@@ -264,7 +264,7 @@ typedef struct FontBLF {
/* the width to wrap the text, see BLF_WORD_WRAP */
int wrap_width;
- /* font dpi (default 72). */
+ /* Font DPI (default 72). */
unsigned int dpi;
/* font size. */
@@ -276,7 +276,8 @@ typedef struct FontBLF {
/* font options. */
int flags;
- /* List of glyph caches (GlyphCacheBLF) for this font for size, dpi, bold, italic.
+ /**
+ * List of glyph caches (#GlyphCacheBLF) for this font for size, DPI, bold, italic.
* Use blf_glyph_cache_acquire(font) and blf_glyph_cache_release(font) to access cache!
*/
ListBase cache;
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 15fdc73adeb..ab13a2e85d0 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -17,15 +17,15 @@ extern "C" {
*/
/* Blender major and minor version. */
-#define BLENDER_VERSION 302
+#define BLENDER_VERSION 303
/* Blender patch version for bugfix releases. */
#define BLENDER_VERSION_PATCH 0
/** Blender release cycle stage: alpha/beta/rc/release. */
-#define BLENDER_VERSION_CYCLE beta
+#define BLENDER_VERSION_CYCLE alpha
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 14
+#define BLENDER_FILE_SUBVERSION 0
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenkernel/BKE_colortools.h b/source/blender/blenkernel/BKE_colortools.h
index 0d4560207ea..d52fd91ccdd 100644
--- a/source/blender/blenkernel/BKE_colortools.h
+++ b/source/blender/blenkernel/BKE_colortools.h
@@ -129,6 +129,36 @@ bool BKE_curvemapping_RGBA_does_something(const struct CurveMapping *cumap);
void BKE_curvemapping_table_F(const struct CurveMapping *cumap, float **array, int *size);
void BKE_curvemapping_table_RGBA(const struct CurveMapping *cumap, float **array, int *size);
+/** Get the minimum x value of each curve map table. */
+void BKE_curvemapping_get_range_minimums(const struct CurveMapping *curve_mapping,
+ float minimums[4]);
+
+/** Get the reciprocal of the difference between the maximum and the minimum x value of each curve
+ * map table. Evaluation parameters can be multiplied by this value to be normalized. If the
+ * difference is zero, 1^8 is returned. */
+void BKE_curvemapping_compute_range_dividers(const struct CurveMapping *curve_mapping,
+ float dividers[4]);
+
+/** Compute the slopes at the start and end points of each curve map. The slopes are multiplied by
+ * the range of the curve map to compensate for parameter normalization. If the slope is vertical,
+ * 1^8 is returned. */
+void BKE_curvemapping_compute_slopes(const struct CurveMapping *curve_mapping,
+ float start_slopes[4],
+ float end_slopes[4]);
+
+/** Check if the curve map at the index is identity, that is, does nothing. A curve map is said to
+ * be identity if:
+ * - The curve mapping uses extrapolation.
+ * - Its range is 1.
+ * - The slope at its start point is 1.
+ * - The slope at its end point is 1.
+ * - The number of points is 2.
+ * - The start point is at (0, 0).
+ * - The end point is at (1, 1).
+ * Note that this could return false even if the curve map is identity, this happens in the case
+ * when more than 2 points exist in the curve map but all points are collinear. */
+bool BKE_curvemapping_is_map_identity(const struct CurveMapping *curve_mapping, int index);
+
/**
* Call when you do images etc, needs restore too. also verifies tables.
* non-const (these modify the curve).
diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
index 1e96c0e4c41..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_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_image.h b/source/blender/blenkernel/BKE_image.h
index 42d0e66cf49..25f20b82900 100644
--- a/source/blender/blenkernel/BKE_image.h
+++ b/source/blender/blenkernel/BKE_image.h
@@ -454,7 +454,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_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..3e06a9d9e9c 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.
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index 881e86ccc54..4749aab4379 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -102,6 +102,7 @@ typedef enum {
/** Accepts #BMesh input (without conversion). */
eModifierTypeFlag_AcceptsBMesh = (1 << 11),
} ModifierTypeFlag;
+ENUM_OPERATORS(ModifierTypeFlag, eModifierTypeFlag_AcceptsBMesh)
typedef void (*IDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cb_flag);
typedef void (*TexWalkFunc)(void *userData,
@@ -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,
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index c9228db9ecc..1ff10d06b00 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -1090,8 +1090,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define SH_NODE_SQUEEZE 117
//#define SH_NODE_MATERIAL_EXT 118
#define SH_NODE_INVERT 119
-#define SH_NODE_SEPRGB 120
-#define SH_NODE_COMBRGB 121
+#define SH_NODE_SEPRGB_LEGACY 120
+#define SH_NODE_COMBRGB_LEGACY 121
#define SH_NODE_HUE_SAT 122
#define SH_NODE_OUTPUT_MATERIAL 124
@@ -1147,8 +1147,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define SH_NODE_WAVELENGTH 180
#define SH_NODE_BLACKBODY 181
#define SH_NODE_VECT_TRANSFORM 182
-#define SH_NODE_SEPHSV 183
-#define SH_NODE_COMBHSV 184
+#define SH_NODE_SEPHSV_LEGACY 183
+#define SH_NODE_COMBHSV_LEGACY 184
#define SH_NODE_BSDF_HAIR 185
// #define SH_NODE_LAMP 186
#define SH_NODE_UVMAP 187
@@ -1175,6 +1175,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define SH_NODE_VECTOR_ROTATE 708
#define SH_NODE_CURVE_FLOAT 709
#define SH_NODE_POINT_INFO 710
+#define SH_NODE_COMBINE_COLOR 711
+#define SH_NODE_SEPARATE_COLOR 712
/** \} */
@@ -1202,8 +1204,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define CMP_NODE_MAP_VALUE 213
#define CMP_NODE_TIME 214
#define CMP_NODE_VECBLUR 215
-#define CMP_NODE_SEPRGBA 216
-#define CMP_NODE_SEPHSVA 217
+#define CMP_NODE_SEPRGBA_LEGACY 216
+#define CMP_NODE_SEPHSVA_LEGACY 217
#define CMP_NODE_SETALPHA 218
#define CMP_NODE_HUE_SAT 219
#define CMP_NODE_IMAGE 220
@@ -1213,14 +1215,14 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define CMP_NODE_TEXTURE 224
#define CMP_NODE_TRANSLATE 225
#define CMP_NODE_ZCOMBINE 226
-#define CMP_NODE_COMBRGBA 227
+#define CMP_NODE_COMBRGBA_LEGACY 227
#define CMP_NODE_DILATEERODE 228
#define CMP_NODE_ROTATE 229
#define CMP_NODE_SCALE 230
-#define CMP_NODE_SEPYCCA 231
-#define CMP_NODE_COMBYCCA 232
-#define CMP_NODE_SEPYUVA 233
-#define CMP_NODE_COMBYUVA 234
+#define CMP_NODE_SEPYCCA_LEGACY 231
+#define CMP_NODE_COMBYCCA_LEGACY 232
+#define CMP_NODE_SEPYUVA_LEGACY 233
+#define CMP_NODE_COMBYUVA_LEGACY 234
#define CMP_NODE_DIFF_MATTE 235
#define CMP_NODE_COLOR_SPILL 236
#define CMP_NODE_CHROMA_MATTE 237
@@ -1232,7 +1234,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define CMP_NODE_ID_MASK 243
#define CMP_NODE_DEFOCUS 244
#define CMP_NODE_DISPLACE 245
-#define CMP_NODE_COMBHSVA 246
+#define CMP_NODE_COMBHSVA_LEGACY 246
#define CMP_NODE_MATH 247
#define CMP_NODE_LUMA_MATTE 248
#define CMP_NODE_BRIGHTCONTRAST 249
@@ -1289,6 +1291,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define CMP_NODE_SCENE_TIME 329
#define CMP_NODE_SEPARATE_XYZ 330
#define CMP_NODE_COMBINE_XYZ 331
+#define CMP_NODE_COMBINE_COLOR 332
+#define CMP_NODE_SEPARATE_COLOR 333
/* channel toggles */
#define CMP_CHAN_RGB 1
@@ -1354,11 +1358,13 @@ struct TexResult;
#define TEX_NODE_TRANSLATE 416
#define TEX_NODE_COORD 417
#define TEX_NODE_DISTANCE 418
-#define TEX_NODE_COMPOSE 419
-#define TEX_NODE_DECOMPOSE 420
+#define TEX_NODE_COMPOSE_LEGACY 419
+#define TEX_NODE_DECOMPOSE_LEGACY 420
#define TEX_NODE_VALTONOR 421
#define TEX_NODE_SCALE 422
#define TEX_NODE_AT 423
+#define TEX_NODE_COMBINE_COLOR 424
+#define TEX_NODE_SEPARATE_COLOR 425
/* 501-599 reserved. Use like this: TEX_NODE_PROC + TEX_CLOUDS, etc */
#define TEX_NODE_PROC 500
@@ -1511,6 +1517,8 @@ struct TexResult;
#define FN_NODE_REPLACE_STRING 1218
#define FN_NODE_INPUT_BOOL 1219
#define FN_NODE_INPUT_INT 1220
+#define FN_NODE_SEPARATE_COLOR 1221
+#define FN_NODE_COMBINE_COLOR 1222
/** \} */
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 0e976f04dd1..03cbcf575c3 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -733,7 +733,7 @@ enum {
/* paint_vertex.cc */
/**
- * Fills the object's active color atribute layer with the fill color.
+ * Fills the object's active color attribute layer with the fill color.
*
* \param[in] ob: The object.
* \param[in] fill_color: The fill color.
diff --git a/source/blender/blenkernel/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..c18e8a2986c 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 ============================ */
+/* --------------------------------------------------------------------
+ * Displacemnt 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_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/anonymous_attribute.cc b/source/blender/blenkernel/intern/anonymous_attribute.cc
index 6ce6bee547c..636e0af0edf 100644
--- a/source/blender/blenkernel/intern/anonymous_attribute.cc
+++ b/source/blender/blenkernel/intern/anonymous_attribute.cc
@@ -41,7 +41,7 @@ static std::string get_new_internal_name()
{
static std::atomic<int> index = 0;
const int next_index = index.fetch_add(1);
- return "anonymous_attribute_" + std::to_string(next_index);
+ return ".a_" + std::to_string(next_index);
}
AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name)
diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc
index 11f36e32b74..81eb1786322 100644
--- a/source/blender/blenkernel/intern/asset_catalog_test.cc
+++ b/source/blender/blenkernel/intern/asset_catalog_test.cc
@@ -318,7 +318,7 @@ TEST_F(AssetCatalogTest, load_catalog_path_backslashes)
const AssetCatalog *found_by_id = service.find_catalog(UUID_POSES_ELLIE_BACKSLASHES);
ASSERT_NE(nullptr, found_by_id);
EXPECT_EQ(AssetCatalogPath("character/Ellie/backslashes"), found_by_id->path)
- << "Backslashes should be normalised when loading from disk.";
+ << "Backslashes should be normalized when loading from disk.";
EXPECT_EQ(StringRefNull("Windows For Life!"), found_by_id->simple_name);
const AssetCatalog *found_by_path = service.find_catalog_by_path("character/Ellie/backslashes");
diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c
index 0cb0704ff80..7f93eb7b393 100644
--- a/source/blender/blenkernel/intern/attribute.c
+++ b/source/blender/blenkernel/intern/attribute.c
@@ -75,9 +75,9 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM])
case ID_CV: {
Curves *curves = (Curves *)id;
info[ATTR_DOMAIN_POINT].customdata = &curves->geometry.point_data;
- info[ATTR_DOMAIN_POINT].length = curves->geometry.point_size;
+ info[ATTR_DOMAIN_POINT].length = curves->geometry.point_num;
info[ATTR_DOMAIN_CURVE].customdata = &curves->geometry.curve_data;
- info[ATTR_DOMAIN_CURVE].length = curves->geometry.curve_size;
+ info[ATTR_DOMAIN_CURVE].length = curves->geometry.curve_num;
break;
}
default:
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index d33b64c493b..e86bac21084 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -184,16 +184,16 @@ static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer
static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data,
const CustomDataType data_type,
- const int domain_size,
+ const int domain_num,
const AttributeInit &initializer)
{
switch (initializer.type) {
case AttributeInit::Type::Default: {
- void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size);
+ void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_num);
return data != nullptr;
}
case AttributeInit::Type::VArray: {
- void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size);
+ void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_num);
if (data == nullptr) {
return false;
}
@@ -204,7 +204,7 @@ static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data
case AttributeInit::Type::MoveArray: {
void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
void *data = CustomData_add_layer(
- &custom_data, data_type, CD_ASSIGN, source_data, domain_size);
+ &custom_data, data_type, CD_ASSIGN, source_data, domain_num);
if (data == nullptr) {
MEM_freeN(source_data);
return false;
@@ -221,35 +221,35 @@ static void *add_generic_custom_data_layer(CustomData &custom_data,
const CustomDataType data_type,
const eCDAllocType alloctype,
void *layer_data,
- const int domain_size,
+ const int domain_num,
const AttributeIDRef &attribute_id)
{
if (attribute_id.is_named()) {
char attribute_name_c[MAX_NAME];
attribute_id.name().copy(attribute_name_c);
return CustomData_add_layer_named(
- &custom_data, data_type, alloctype, layer_data, domain_size, attribute_name_c);
+ &custom_data, data_type, alloctype, layer_data, domain_num, attribute_name_c);
}
const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id();
return CustomData_add_layer_anonymous(
- &custom_data, data_type, alloctype, layer_data, domain_size, &anonymous_id);
+ &custom_data, data_type, alloctype, layer_data, domain_num, &anonymous_id);
}
static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attribute_id,
CustomData &custom_data,
const CustomDataType data_type,
- const int domain_size,
+ const int domain_num,
const AttributeInit &initializer)
{
switch (initializer.type) {
case AttributeInit::Type::Default: {
void *data = add_generic_custom_data_layer(
- custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id);
+ custom_data, data_type, CD_DEFAULT, nullptr, domain_num, attribute_id);
return data != nullptr;
}
case AttributeInit::Type::VArray: {
void *data = add_generic_custom_data_layer(
- custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id);
+ custom_data, data_type, CD_DEFAULT, nullptr, domain_num, attribute_id);
if (data == nullptr) {
return false;
}
@@ -260,7 +260,7 @@ static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attr
case AttributeInit::Type::MoveArray: {
void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
void *data = add_generic_custom_data_layer(
- custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_id);
+ custom_data, data_type, CD_ASSIGN, source_data, domain_num, attribute_id);
if (data == nullptr) {
MEM_freeN(source_data);
return false;
@@ -303,8 +303,8 @@ GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const GeometryComponent
return {};
}
- const int domain_size = component.attribute_domain_size(domain_);
- return as_read_attribute_(data, domain_size);
+ const int domain_num = component.attribute_domain_num(domain_);
+ return as_read_attribute_(data, domain_num);
}
WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write(
@@ -317,7 +317,7 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write(
if (custom_data == nullptr) {
return {};
}
- const int domain_size = component.attribute_domain_size(domain_);
+ const int domain_num = component.attribute_domain_num(domain_);
void *data;
if (stored_as_named_attribute_) {
@@ -333,10 +333,10 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write(
void *new_data;
if (stored_as_named_attribute_) {
new_data = CustomData_duplicate_referenced_layer_named(
- custom_data, stored_type_, name_.c_str(), domain_size);
+ custom_data, stored_type_, name_.c_str(), domain_num);
}
else {
- new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_size);
+ new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_num);
}
if (data != new_data) {
@@ -353,7 +353,7 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write(
};
}
- return {as_write_attribute_(data, domain_size), domain_, std::move(tag_modified_fn)};
+ return {as_write_attribute_(data, domain_num), domain_, std::move(tag_modified_fn)};
}
bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) const
@@ -366,7 +366,7 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co
return {};
}
- const int domain_size = component.attribute_domain_size(domain_);
+ const int domain_num = component.attribute_domain_num(domain_);
int layer_index;
if (stored_as_named_attribute_) {
for (const int i : IndexRange(custom_data->totlayer)) {
@@ -381,7 +381,7 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co
}
const bool delete_success = CustomData_free_layer(
- custom_data, stored_type_, domain_size, layer_index);
+ custom_data, stored_type_, domain_num, layer_index);
if (delete_success) {
if (custom_data_access_.update_custom_data_pointers) {
custom_data_access_.update_custom_data_pointers(component);
@@ -401,7 +401,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component,
return false;
}
- const int domain_size = component.attribute_domain_size(domain_);
+ const int domain_num = component.attribute_domain_num(domain_);
bool success;
if (stored_as_named_attribute_) {
if (CustomData_get_layer_named(custom_data, data_type_, name_.c_str())) {
@@ -409,7 +409,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component,
return false;
}
success = add_custom_data_layer_from_attribute_init(
- name_, *custom_data, stored_type_, domain_size, initializer);
+ name_, *custom_data, stored_type_, domain_num, initializer);
}
else {
if (CustomData_get_layer(custom_data, stored_type_) != nullptr) {
@@ -417,7 +417,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component,
return false;
}
success = add_builtin_type_custom_data_layer_from_init(
- *custom_data, stored_type_, domain_size, initializer);
+ *custom_data, stored_type_, domain_num, initializer);
}
if (success) {
if (custom_data_access_.update_custom_data_pointers) {
@@ -446,7 +446,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
if (custom_data == nullptr) {
return {};
}
- const int domain_size = component.attribute_domain_size(domain_);
+ const int domain_num = component.attribute_domain_num(domain_);
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) {
continue;
@@ -455,7 +455,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
if (type == nullptr) {
continue;
}
- GSpan data{*type, layer.data, domain_size};
+ GSpan data{*type, layer.data, domain_num};
return {GVArray::ForSpan(data), domain_};
}
return {};
@@ -468,24 +468,23 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
if (custom_data == nullptr) {
return {};
}
- const int domain_size = component.attribute_domain_size(domain_);
+ const int domain_num = component.attribute_domain_num(domain_);
for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) {
continue;
}
if (attribute_id.is_named()) {
- CustomData_duplicate_referenced_layer_named(
- custom_data, layer.type, layer.name, domain_size);
+ CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_num);
}
else {
CustomData_duplicate_referenced_layer_anonymous(
- custom_data, layer.type, &attribute_id.anonymous_id(), domain_size);
+ custom_data, layer.type, &attribute_id.anonymous_id(), domain_num);
}
const CPPType *type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
if (type == nullptr) {
continue;
}
- GMutableSpan data{*type, layer.data, domain_size};
+ GMutableSpan data{*type, layer.data, domain_num};
return {GVMutableArray::ForSpan(data), domain_};
}
return {};
@@ -498,12 +497,12 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
if (custom_data == nullptr) {
return false;
}
- const int domain_size = component.attribute_domain_size(domain_);
+ const int domain_num = component.attribute_domain_num(domain_);
for (const int i : IndexRange(custom_data->totlayer)) {
const CustomDataLayer &layer = custom_data->layers[i];
if (this->type_is_supported((CustomDataType)layer.type) &&
custom_data_layer_matches_attribute_id(layer, attribute_id)) {
- CustomData_free_layer(custom_data, layer.type, domain_size, i);
+ CustomData_free_layer(custom_data, layer.type, domain_num, i);
return true;
}
}
@@ -531,9 +530,9 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
return false;
}
}
- const int domain_size = component.attribute_domain_size(domain_);
+ const int domain_num = component.attribute_domain_num(domain_);
add_custom_data_layer_from_attribute_init(
- attribute_id, *custom_data, data_type, domain_size, initializer);
+ attribute_id, *custom_data, data_type, domain_num, initializer);
return true;
}
@@ -567,8 +566,8 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read(
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
if (layer.type == stored_type_) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
- const int domain_size = component.attribute_domain_size(domain_);
- return {as_read_attribute_(layer.data, domain_size), domain_};
+ const int domain_num = component.attribute_domain_num(domain_);
+ return {as_read_attribute_(layer.data, domain_num), domain_};
}
}
}
@@ -585,16 +584,16 @@ WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write(
for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
if (layer.type == stored_type_) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
- const int domain_size = component.attribute_domain_size(domain_);
+ const int domain_num = component.attribute_domain_num(domain_);
void *data_old = layer.data;
void *data_new = CustomData_duplicate_referenced_layer_named(
- custom_data, stored_type_, layer.name, domain_size);
+ custom_data, stored_type_, layer.name, domain_num);
if (data_old != data_new) {
if (custom_data_access_.update_custom_data_pointers) {
custom_data_access_.update_custom_data_pointers(component);
}
}
- return {as_write_attribute_(layer.data, domain_size), domain_};
+ return {as_write_attribute_(layer.data, domain_num), domain_};
}
}
}
@@ -612,8 +611,8 @@ bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component,
const CustomDataLayer &layer = custom_data->layers[i];
if (layer.type == stored_type_) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
- const int domain_size = component.attribute_domain_size(domain_);
- CustomData_free_layer(custom_data, stored_type_, domain_size, i);
+ const int domain_num = component.attribute_domain_num(domain_);
+ CustomData_free_layer(custom_data, stored_type_, domain_num, i);
if (custom_data_access_.update_custom_data_pointers) {
custom_data_access_.update_custom_data_pointers(component);
}
@@ -702,9 +701,9 @@ GVArray CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id,
std::optional<GSpan> attribute = this->get_for_read(attribute_id);
if (!attribute) {
- const int domain_size = this->size_;
+ const int domain_num = this->size_;
return GVArray::ForSingle(
- *type, domain_size, (default_value == nullptr) ? type->default_value() : default_value);
+ *type, domain_num, (default_value == nullptr) ? type->default_value() : default_value);
}
if (attribute->type() == *type) {
@@ -822,7 +821,7 @@ bool GeometryComponent::attribute_domain_supported(const AttributeDomain domain)
return providers->supported_domains().contains(domain);
}
-int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain)) const
+int GeometryComponent::attribute_domain_num(const AttributeDomain UNUSED(domain)) const
{
return 0;
}
@@ -1157,8 +1156,8 @@ blender::GVArray GeometryComponent::attribute_get_for_read(const AttributeIDRef
if (default_value == nullptr) {
default_value = type->default_value();
}
- const int domain_size = this->attribute_domain_size(domain);
- return blender::GVArray::ForSingle(*type, domain_size, default_value);
+ const int domain_num = this->attribute_domain_num(domain);
+ return blender::GVArray::ForSingle(*type, domain_num, default_value);
}
class GVMutableAttribute_For_OutputAttribute : public blender::GVArrayImpl_For_GSpan {
@@ -1267,10 +1266,10 @@ static OutputAttribute create_output_attribute(GeometryComponent &component,
WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
if (!attribute) {
if (default_value) {
- const int64_t domain_size = component.attribute_domain_size(domain);
+ const int64_t domain_num = component.attribute_domain_num(domain);
component.attribute_try_create_builtin(
attribute_name,
- AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_size, default_value)));
+ AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_num, default_value)));
}
else {
component.attribute_try_create_builtin(attribute_name, AttributeInitDefault());
@@ -1301,7 +1300,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component,
ignore_old_values);
}
- const int domain_size = component.attribute_domain_size(domain);
+ const int domain_num = component.attribute_domain_num(domain);
WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_id);
if (!attribute) {
@@ -1310,7 +1309,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component,
attribute_id,
domain,
data_type,
- AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_size, default_value)));
+ AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_num, default_value)));
}
else {
component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault());
@@ -1333,8 +1332,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component,
/* Allocate a new array that lives next to the existing attribute. It will overwrite the existing
* attribute after processing is done. */
- void *data = MEM_mallocN_aligned(
- cpp_type->size() * domain_size, cpp_type->alignment(), __func__);
+ void *data = MEM_mallocN_aligned(cpp_type->size() * domain_num, cpp_type->alignment(), __func__);
if (ignore_old_values) {
/* This does nothing for trivially constructible types, but is necessary for correctness. */
cpp_type->default_construct_n(data, domain);
@@ -1343,10 +1341,10 @@ static OutputAttribute create_output_attribute(GeometryComponent &component,
/* Fill the temporary array with values from the existing attribute. */
GVArray old_varray = component.attribute_get_for_read(
attribute_id, domain, data_type, default_value);
- old_varray.materialize_to_uninitialized(IndexRange(domain_size), data);
+ old_varray.materialize_to_uninitialized(IndexRange(domain_num), data);
}
GVMutableArray varray = GVMutableArray::For<GVMutableAttribute_For_OutputAttribute>(
- GMutableSpan{*cpp_type, data, domain_size}, component, attribute_id);
+ GMutableSpan{*cpp_type, data, domain_num}, component, attribute_id);
return OutputAttribute(std::move(varray), domain, save_output_attribute, true);
}
@@ -1429,7 +1427,7 @@ GVArray IDAttributeFieldInput::get_varray_for_context(const GeometryComponent &c
const StringRef name = get_random_id_attribute_name(domain);
GVArray attribute = component.attribute_try_get_for_read(name, domain, CD_PROP_INT32);
if (attribute) {
- BLI_assert(attribute.size() == component.attribute_domain_size(domain));
+ BLI_assert(attribute.size() == component.attribute_domain_num(domain));
return attribute;
}
diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh
index 8c021ed0e21..f0f47cb7a11 100644
--- a/source/blender/blenkernel/intern/attribute_access_intern.hh
+++ b/source/blender/blenkernel/intern/attribute_access_intern.hh
@@ -172,8 +172,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
*/
class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
private:
- using AsReadAttribute = GVArray (*)(const void *data, int domain_size);
- using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_size);
+ using AsReadAttribute = GVArray (*)(const void *data, int domain_num);
+ using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_num);
const AttributeDomain domain_;
const CustomDataType attribute_type_;
const CustomDataType stored_type_;
@@ -207,14 +207,14 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final;
};
-template<typename T> GVArray make_array_read_attribute(const void *data, const int domain_size)
+template<typename T> GVArray make_array_read_attribute(const void *data, const int domain_num)
{
- return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size));
+ return VArray<T>::ForSpan(Span<T>((const T *)data, domain_num));
}
-template<typename T> GVMutableArray make_array_write_attribute(void *data, const int domain_size)
+template<typename T> GVMutableArray make_array_write_attribute(void *data, const int domain_num)
{
- return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size));
+ return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_num));
}
/**
@@ -226,8 +226,8 @@ template<typename T> GVMutableArray make_array_write_attribute(void *data, const
* if the stored type is the same as the attribute type.
*/
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
- using AsReadAttribute = GVArray (*)(const void *data, int domain_size);
- using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_size);
+ using AsReadAttribute = GVArray (*)(const void *data, int domain_num);
+ using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_num);
using UpdateOnRead = void (*)(const GeometryComponent &component);
using UpdateOnWrite = void (*)(GeometryComponent &component);
const CustomDataType stored_type_;
diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c
index 555c4690308..e2f22ef00d3 100644
--- a/source/blender/blenkernel/intern/blendfile_link_append.c
+++ b/source/blender/blenkernel/intern/blendfile_link_append.c
@@ -1564,7 +1564,7 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context,
}
if (GS(old_id->name) == ID_KE) {
- /* Shape Keys are handled as part of their owning obdata (see below). This implies thar
+ /* Shape Keys are handled as part of their owning obdata (see below). This implies that
* there is no way to know when the old pointer gets invalid, so just clear it immediately.
*/
item->userdata = NULL;
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index 0593db34e20..e8c95869910 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -1559,6 +1559,7 @@ void BKE_brush_init_curves_sculpt_settings(Brush *brush)
}
BrushCurvesSculptSettings *settings = brush->curves_sculpt_settings;
settings->add_amount = 1;
+ settings->points_per_curve = 8;
settings->minimum_length = 0.01f;
settings->curve_length = 0.3f;
}
diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c
index c3d66d4463d..e4c46703f8a 100644
--- a/source/blender/blenkernel/intern/colortools.c
+++ b/source/blender/blenkernel/intern/colortools.c
@@ -1158,6 +1158,80 @@ bool BKE_curvemapping_RGBA_does_something(const CurveMapping *cumap)
return false;
}
+void BKE_curvemapping_get_range_minimums(const CurveMapping *curve_mapping, float minimums[CM_TOT])
+{
+ for (int i = 0; i < CM_TOT; i++) {
+ minimums[i] = curve_mapping->cm[i].mintable;
+ }
+}
+
+void BKE_curvemapping_compute_range_dividers(const CurveMapping *curve_mapping,
+ float dividers[CM_TOT])
+{
+ for (int i = 0; i < CM_TOT; i++) {
+ const CurveMap *curve_map = &curve_mapping->cm[i];
+ dividers[i] = 1.0f / max_ff(1e-8f, curve_map->maxtable - curve_map->mintable);
+ }
+}
+
+void BKE_curvemapping_compute_slopes(const CurveMapping *curve_mapping,
+ float start_slopes[CM_TOT],
+ float end_slopes[CM_TOT])
+{
+ float range_dividers[CM_TOT];
+ BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
+ for (int i = 0; i < CM_TOT; i++) {
+ const CurveMap *curve_map = &curve_mapping->cm[i];
+ /* If extrapolation is not enabled, the slopes are horizontal. */
+ if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) {
+ start_slopes[i] = 0.0f;
+ end_slopes[i] = 0.0f;
+ continue;
+ }
+
+ if (curve_map->ext_in[0] != 0.0f) {
+ start_slopes[i] = curve_map->ext_in[1] / (curve_map->ext_in[0] * range_dividers[i]);
+ }
+ else {
+ start_slopes[i] = 1e8f;
+ }
+
+ if (curve_map->ext_out[0] != 0.0f) {
+ end_slopes[i] = curve_map->ext_out[1] / (curve_map->ext_out[0] * range_dividers[i]);
+ }
+ else {
+ end_slopes[i] = 1e8f;
+ }
+ }
+}
+
+bool BKE_curvemapping_is_map_identity(const CurveMapping *curve_mapping, int index)
+{
+ if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) {
+ return false;
+ }
+ const CurveMap *curve_map = &curve_mapping->cm[index];
+ if (curve_map->maxtable - curve_map->mintable != 1.0f) {
+ return false;
+ }
+ if (curve_map->ext_in[0] != curve_map->ext_in[1]) {
+ return false;
+ }
+ if (curve_map->ext_out[0] != curve_map->ext_out[1]) {
+ return false;
+ }
+ if (curve_map->totpoint != 2) {
+ return false;
+ }
+ if (curve_map->curve[0].x != 0 || curve_map->curve[0].y != 0) {
+ return false;
+ }
+ if (curve_map->curve[1].x != 0 || curve_map->curve[1].y != 0) {
+ return false;
+ }
+ return true;
+}
+
void BKE_curvemapping_init(CurveMapping *cumap)
{
int a;
diff --git a/source/blender/blenkernel/intern/curve_catmull_rom.cc b/source/blender/blenkernel/intern/curve_catmull_rom.cc
index 2db183eea3e..4b2174c912c 100644
--- a/source/blender/blenkernel/intern/curve_catmull_rom.cc
+++ b/source/blender/blenkernel/intern/curve_catmull_rom.cc
@@ -9,11 +9,11 @@
namespace blender::bke::curves::catmull_rom {
-int calculate_evaluated_size(const int points_num, const bool cyclic, const int resolution)
+int calculate_evaluated_num(const int points_num, const bool cyclic, const int resolution)
{
- const int eval_size = resolution * curve_segment_size(points_num, cyclic);
+ const int eval_num = resolution * curve_segment_num(points_num, cyclic);
/* If the curve isn't cyclic, one last point is added to the final point. */
- return cyclic ? eval_size : eval_size + 1;
+ return cyclic ? eval_num : eval_num + 1;
}
/* Adapted from Cycles #catmull_rom_basis_eval function. */
@@ -46,7 +46,7 @@ static void interpolate_to_evaluated(const Span<T> src,
MutableSpan<T> dst)
{
- BLI_assert(dst.size() == calculate_evaluated_size(src.size(), cyclic, resolution));
+ BLI_assert(dst.size() == calculate_evaluated_num(src.size(), cyclic, resolution));
/* - First deal with one and two point curves need special attention.
* - Then evaluate the first and last segment(s) whose control points need to wrap around
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
index 3d9dd3ecf31..c507e7934a8 100644
--- a/source/blender/blenkernel/intern/curve_eval.cc
+++ b/source/blender/blenkernel/intern/curve_eval.cc
@@ -117,7 +117,7 @@ float CurveEval::total_length() const
return length;
}
-int CurveEval::total_control_point_size() const
+int CurveEval::total_control_point_num() const
{
int count = 0;
for (const SplinePtr &spline : this->splines()) {
@@ -144,7 +144,7 @@ blender::Array<int> CurveEval::evaluated_point_offsets() const
int offset = 0;
for (const int i : splines_.index_range()) {
offsets[i] = offset;
- offset += splines_[i]->evaluated_points_size();
+ offset += splines_[i]->evaluated_points_num();
}
offsets.last() = offset;
return offsets;
@@ -463,7 +463,7 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves)
Curves *curve_eval_to_curves(const CurveEval &curve_eval)
{
- Curves *curves = blender::bke::curves_new_nomain(curve_eval.total_control_point_size(),
+ Curves *curves = blender::bke::curves_new_nomain(curve_eval.total_control_point_num(),
curve_eval.splines().size());
CurveComponent dst_component;
dst_component.replace(curves, GeometryOwnershipType::Editable);
diff --git a/source/blender/blenkernel/intern/curve_nurbs.cc b/source/blender/blenkernel/intern/curve_nurbs.cc
index 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..84ba98db54b 100644
--- a/source/blender/blenkernel/intern/curves.cc
+++ b/source/blender/blenkernel/intern/curves.cc
@@ -78,12 +78,12 @@ static void curves_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src,
* shallow copy from the source to the destination, and because the copy-on-write functionality
* isn't supported more generically yet. */
- dst.point_size = src.point_size;
- dst.curve_size = src.curve_size;
+ dst.point_num = src.point_num;
+ dst.curve_num = src.curve_num;
const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE;
- CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, alloc_type, dst.point_size);
- CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, alloc_type, dst.curve_size);
+ CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, alloc_type, dst.point_num);
+ CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, alloc_type, dst.curve_num);
dst.curve_offsets = static_cast<int *>(MEM_dupallocN(src.curve_offsets));
@@ -136,17 +136,17 @@ static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_addre
CustomData_blend_write(writer,
&curves->geometry.point_data,
players,
- curves->geometry.point_size,
+ curves->geometry.point_num,
CD_MASK_ALL,
&curves->id);
CustomData_blend_write(writer,
&curves->geometry.curve_data,
clayers,
- curves->geometry.curve_size,
+ curves->geometry.curve_num,
CD_MASK_ALL,
&curves->id);
- BLO_write_int32_array(writer, curves->geometry.curve_size + 1, curves->geometry.curve_offsets);
+ BLO_write_int32_array(writer, curves->geometry.curve_num + 1, curves->geometry.curve_offsets);
BLO_write_pointer_array(writer, curves->totcol, curves->mat);
if (curves->adt) {
@@ -169,11 +169,11 @@ static void curves_blend_read_data(BlendDataReader *reader, ID *id)
BKE_animdata_blend_read_data(reader, curves->adt);
/* Geometry */
- CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_size);
- CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_size);
+ CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_num);
+ CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_num);
update_custom_data_pointers(*curves);
- BLO_read_int32_array(reader, curves->geometry.curve_size + 1, &curves->geometry.curve_offsets);
+ BLO_read_int32_array(reader, curves->geometry.curve_num + 1, &curves->geometry.curve_offsets);
curves->geometry.runtime = MEM_new<blender::bke::CurvesGeometryRuntime>(__func__);
@@ -379,4 +379,11 @@ Curves *curves_new_nomain_single(const int points_num, const CurveType type)
return curves;
}
+Curves *curves_new_nomain(CurvesGeometry curves)
+{
+ Curves *curves_id = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr));
+ bke::CurvesGeometry::wrap(curves_id->geometry) = std::move(curves);
+ return curves_id;
+}
+
} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc
index 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/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
index f409389e463..a28afc8ddca 100644
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -114,7 +114,7 @@ void CurveComponentLegacy::ensure_owns_direct_data()
/** \name Attribute Access Helper Functions
* \{ */
-int CurveComponentLegacy::attribute_domain_size(const AttributeDomain domain) const
+int CurveComponentLegacy::attribute_domain_num(const AttributeDomain domain) const
{
if (curve_ == nullptr) {
return 0;
@@ -251,8 +251,8 @@ template<typename T> class VArray_For_SplineToPoint final : public VArrayImpl<T>
void materialize(const IndexMask mask, MutableSpan<T> r_span) const final
{
- const int total_size = offsets_.last();
- if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+ const int total_num = offsets_.last();
+ if (mask.is_range() && mask.as_range() == IndexRange(total_num)) {
for (const int spline_index : original_data_.index_range()) {
const int offset = offsets_[spline_index];
const int next_offset = offsets_[spline_index + 1];
@@ -273,8 +273,8 @@ template<typename T> class VArray_For_SplineToPoint final : public VArrayImpl<T>
void materialize_to_uninitialized(const IndexMask mask, MutableSpan<T> r_span) const final
{
T *dst = r_span.data();
- const int total_size = offsets_.last();
- if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+ const int total_num = offsets_.last();
+ if (mask.is_range() && mask.as_range() == IndexRange(total_num)) {
for (const int spline_index : original_data_.index_range()) {
const int offset = offsets_[spline_index];
const int next_offset = offsets_[spline_index + 1];
@@ -415,7 +415,7 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
bool exists(const GeometryComponent &component) const final
{
- return component.attribute_domain_size(ATTR_DOMAIN_CURVE) != 0;
+ return component.attribute_domain_num(ATTR_DOMAIN_CURVE) != 0;
}
};
@@ -495,8 +495,8 @@ static void point_attribute_materialize(Span<Span<T>> data,
const IndexMask mask,
MutableSpan<T> r_span)
{
- const int total_size = offsets.last();
- if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+ const int total_num = offsets.last();
+ if (mask.is_range() && mask.as_range() == IndexRange(total_num)) {
for (const int spline_index : data.index_range()) {
const int offset = offsets[spline_index];
const int next_offset = offsets[spline_index + 1];
@@ -541,8 +541,8 @@ static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
MutableSpan<T> r_span)
{
T *dst = r_span.data();
- const int total_size = offsets.last();
- if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+ const int total_num = offsets.last();
+ if (mask.is_range() && mask.as_range() == IndexRange(total_num)) {
for (const int spline_index : data.index_range()) {
const int offset = offsets[spline_index];
const int next_offset = offsets[spline_index + 1];
@@ -589,13 +589,13 @@ static GVArray varray_from_initializer(const AttributeInit &initializer,
case AttributeInit::Type::VArray:
return static_cast<const AttributeInitVArray &>(initializer).varray;
case AttributeInit::Type::MoveArray:
- int total_size = 0;
+ int total_num = 0;
for (const SplinePtr &spline : splines) {
- total_size += spline->size();
+ total_num += spline->size();
}
return GVArray::ForSpan(GSpan(*bke::custom_data_type_to_cpp_type(data_type),
static_cast<const AttributeInitMove &>(initializer).data,
- total_size));
+ total_num));
}
BLI_assert_unreachable();
return {};
@@ -1168,7 +1168,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
}
return curve->has_spline_with_type(CURVE_TYPE_BEZIER) &&
- component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0;
+ component.attribute_domain_num(ATTR_DOMAIN_POINT) != 0;
}
};
diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc
index bc9bba3ee2f..b565143d08f 100644
--- a/source/blender/blenkernel/intern/geometry_component_curves.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curves.cc
@@ -237,6 +237,69 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const Attri
return nullptr;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Curve Length Field Input
+ * \{ */
+
+static VArray<float> construct_curve_length_gvarray(const CurveComponent &component,
+ const AttributeDomain domain)
+{
+ if (!component.has_curves()) {
+ return {};
+ }
+ const Curves &curves_id = *component.get_for_read();
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+
+ curves.ensure_evaluated_lengths();
+
+ VArray<bool> cyclic = curves.cyclic();
+ VArray<float> lengths = VArray<float>::ForFunc(
+ curves.curves_num(), [&curves, cyclic = std::move(cyclic)](int64_t index) {
+ return curves.evaluated_length_total_for_curve(index, cyclic[index]);
+ });
+
+ if (domain == ATTR_DOMAIN_CURVE) {
+ return lengths;
+ }
+
+ if (domain == ATTR_DOMAIN_POINT) {
+ return component.attribute_try_adapt_domain<float>(
+ std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
+ }
+
+ return {};
+}
+
+CurveLengthFieldInput::CurveLengthFieldInput()
+ : GeometryFieldInput(CPPType::get<float>(), "Spline Length node")
+{
+ category_ = Category::Generated;
+}
+
+GVArray CurveLengthFieldInput::get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const
+{
+ if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ return construct_curve_length_gvarray(curve_component, domain);
+ }
+ return {};
+}
+
+uint64_t CurveLengthFieldInput::hash() const
+{
+ /* Some random constant hash. */
+ return 3549623580;
+}
+
+bool CurveLengthFieldInput::is_equal_to(const fn::FieldNode &other) const
+{
+ return dynamic_cast<const CurveLengthFieldInput *>(&other) != nullptr;
+}
+
} // namespace blender::bke
/** \} */
@@ -245,7 +308,7 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const Attri
/** \name Attribute Access Helper Functions
* \{ */
-int CurveComponent::attribute_domain_size(const AttributeDomain domain) const
+int CurveComponent::attribute_domain_num(const AttributeDomain domain) const
{
if (curves_ == nullptr) {
return 0;
diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc
index 0dc6f486d28..e56a7ca4dd8 100644
--- a/source/blender/blenkernel/intern/geometry_component_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_component_instances.cc
@@ -79,7 +79,7 @@ void InstancesComponent::add_instance(const int instance_handle, const float4x4
BLI_assert(instance_handle < references_.size());
instance_reference_handles_.append(instance_handle);
instance_transforms_.append(transform);
- attributes_.reallocate(this->instances_amount());
+ attributes_.reallocate(this->instances_num());
}
blender::Span<int> InstancesComponent::instance_reference_handles() const
@@ -183,7 +183,7 @@ void InstancesComponent::remove_unused_references()
using namespace blender;
using namespace blender::bke;
- const int tot_instances = this->instances_amount();
+ const int tot_instances = this->instances_num();
const int tot_references_before = references_.size();
if (tot_instances == 0) {
@@ -258,12 +258,12 @@ void InstancesComponent::remove_unused_references()
});
}
-int InstancesComponent::instances_amount() const
+int InstancesComponent::instances_num() const
{
return instance_transforms_.size();
}
-int InstancesComponent::references_amount() const
+int InstancesComponent::references_num() const
{
return references_.size();
}
@@ -358,7 +358,7 @@ blender::Span<int> InstancesComponent::almost_unique_ids() const
}
}
else {
- almost_unique_ids_.reinitialize(this->instances_amount());
+ almost_unique_ids_.reinitialize(this->instances_num());
for (const int i : almost_unique_ids_.index_range()) {
almost_unique_ids_[i] = i;
}
@@ -366,12 +366,12 @@ blender::Span<int> InstancesComponent::almost_unique_ids() const
return almost_unique_ids_;
}
-int InstancesComponent::attribute_domain_size(const AttributeDomain domain) const
+int InstancesComponent::attribute_domain_num(const AttributeDomain domain) const
{
if (domain != ATTR_DOMAIN_INSTANCE) {
return 0;
}
- return this->instances_amount();
+ return this->instances_num();
}
blender::bke::CustomDataAttributes &InstancesComponent::attributes()
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
index fb39861d3e7..5ac9a03f43c 100644
--- a/source/blender/blenkernel/intern/geometry_component_mesh.cc
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -169,7 +169,7 @@ VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component,
/** \name Attribute Access
* \{ */
-int MeshComponent::attribute_domain_size(const AttributeDomain domain) const
+int MeshComponent::attribute_domain_num(const AttributeDomain domain) const
{
if (mesh_ == nullptr) {
return 0;
@@ -839,20 +839,20 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com
namespace blender::bke {
template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
-static GVArray make_derived_read_attribute(const void *data, const int domain_size)
+static GVArray make_derived_read_attribute(const void *data, const int domain_num)
{
return VArray<ElemT>::template ForDerivedSpan<StructT, GetFunc>(
- Span<StructT>((const StructT *)data, domain_size));
+ Span<StructT>((const StructT *)data, domain_num));
}
template<typename StructT,
typename ElemT,
ElemT (*GetFunc)(const StructT &),
void (*SetFunc)(StructT &, ElemT)>
-static GVMutableArray make_derived_write_attribute(void *data, const int domain_size)
+static GVMutableArray make_derived_write_attribute(void *data, const int domain_num)
{
return VMutableArray<ElemT>::template ForDerivedSpan<StructT, GetFunc, SetFunc>(
- MutableSpan<StructT>((StructT *)data, domain_size));
+ MutableSpan<StructT>((StructT *)data, domain_num));
}
static float3 get_vertex_position(const MVert &vert)
@@ -1160,7 +1160,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
bool exists(const GeometryComponent &component) const final
{
- return component.attribute_domain_size(ATTR_DOMAIN_FACE) != 0;
+ return component.attribute_domain_num(ATTR_DOMAIN_FACE) != 0;
}
};
diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
index 400e0ea5e15..6de123c7cb9 100644
--- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
+++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
@@ -104,7 +104,7 @@ void PointCloudComponent::ensure_owns_direct_data()
/** \name Attribute Access
* \{ */
-int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) const
+int PointCloudComponent::attribute_domain_num(const AttributeDomain domain) const
{
if (pointcloud_ == nullptr) {
return 0;
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index 2bd8b643899..40e36ced199 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -272,7 +272,7 @@ bool GeometrySet::has_pointcloud() const
bool GeometrySet::has_instances() const
{
const InstancesComponent *component = this->get_component_for_read<InstancesComponent>();
- return component != nullptr && component->instances_amount() >= 1;
+ return component != nullptr && component->instances_num() >= 1;
}
bool GeometrySet::has_volume() const
diff --git a/source/blender/blenkernel/intern/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/main.c b/source/blender/blenkernel/intern/main.c
index 03e03dacfbc..b9ed783fa8c 100644
--- a/source/blender/blenkernel/intern/main.c
+++ b/source/blender/blenkernel/intern/main.c
@@ -502,13 +502,13 @@ BlendThumbnail *BKE_main_thumbnail_from_imbuf(Main *bmain, ImBuf *img)
}
if (img) {
- const size_t sz = BLEN_THUMB_MEMSIZE(img->x, img->y);
- data = MEM_mallocN(sz, __func__);
+ const size_t data_size = BLEN_THUMB_MEMSIZE(img->x, img->y);
+ data = MEM_mallocN(data_size, __func__);
IMB_rect_from_float(img); /* Just in case... */
data->width = img->x;
data->height = img->y;
- memcpy(data->rect, img->rect, sz - sizeof(*data));
+ memcpy(data->rect, img->rect, data_size - sizeof(*data));
}
if (bmain) {
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index 002b496393f..1fbe4f768ff 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -1123,15 +1123,16 @@ void BKE_object_material_remap_calc(Object *ob_dst, Object *ob_src, short *remap
BLI_ghash_free(gh_mat_map, NULL, NULL);
}
-void BKE_object_material_from_eval_data(Main *bmain, Object *ob_orig, ID *data_eval)
+void BKE_object_material_from_eval_data(Main *bmain, Object *ob_orig, const ID *data_eval)
{
ID *data_orig = ob_orig->data;
short *orig_totcol = BKE_id_material_len_p(data_orig);
Material ***orig_mat = BKE_id_material_array_p(data_orig);
- short *eval_totcol = BKE_id_material_len_p(data_eval);
- Material ***eval_mat = BKE_id_material_array_p(data_eval);
+ /* Can cast away const, because the data is not changed. */
+ const short *eval_totcol = BKE_id_material_len_p((ID *)data_eval);
+ Material ***eval_mat = BKE_id_material_array_p((ID *)data_eval);
if (ELEM(NULL, orig_totcol, orig_mat, eval_totcol, eval_mat)) {
return;
diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc
index 628f59ae449..4e3544d0f60 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++) {
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index 200eefb73ec..ffa39f79e7c 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -4502,6 +4502,8 @@ static void registerCompositNodes()
register_node_type_cmp_premulkey();
register_node_type_cmp_separate_xyz();
register_node_type_cmp_combine_xyz();
+ register_node_type_cmp_separate_color();
+ register_node_type_cmp_combine_color();
register_node_type_cmp_diff_matte();
register_node_type_cmp_distance_matte();
@@ -4574,6 +4576,8 @@ static void registerShaderNodes()
register_node_type_sh_vect_transform();
register_node_type_sh_squeeze();
register_node_type_sh_invert();
+ register_node_type_sh_sepcolor();
+ register_node_type_sh_combcolor();
register_node_type_sh_seprgb();
register_node_type_sh_combrgb();
register_node_type_sh_sephsv();
@@ -4660,6 +4664,8 @@ static void registerTextureNodes()
register_node_type_tex_distance();
register_node_type_tex_compose();
register_node_type_tex_decompose();
+ register_node_type_tex_combine_color();
+ register_node_type_tex_separate_color();
register_node_type_tex_output();
register_node_type_tex_viewer();
@@ -4821,6 +4827,7 @@ static void registerFunctionNodes()
{
register_node_type_fn_align_euler_to_vector();
register_node_type_fn_boolean_math();
+ register_node_type_fn_combine_color();
register_node_type_fn_compare();
register_node_type_fn_float_to_int();
register_node_type_fn_input_bool();
@@ -4832,6 +4839,7 @@ static void registerFunctionNodes()
register_node_type_fn_random_value();
register_node_type_fn_replace_string();
register_node_type_fn_rotate_euler();
+ register_node_type_fn_separate_color();
register_node_type_fn_slice_string();
register_node_type_fn_string_length();
register_node_type_fn_value_to_string();
diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc
index 2a25d73ed87..7f5146f14e0 100644
--- a/source/blender/blenkernel/intern/object.cc
+++ b/source/blender/blenkernel/intern/object.cc
@@ -2114,7 +2114,7 @@ static const char *get_obdata_defname(int type)
case OB_SPEAKER:
return DATA_("Speaker");
case OB_CURVES:
- return DATA_("HairCurves");
+ return DATA_("Curves");
case OB_POINTCLOUD:
return DATA_("PointCloud");
case OB_VOLUME:
diff --git a/source/blender/blenkernel/intern/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/pbvh_pixels.cc b/source/blender/blenkernel/intern/pbvh_pixels.cc
index 5623cac44ac..8b3fcffd9cd 100644
--- a/source/blender/blenkernel/intern/pbvh_pixels.cc
+++ b/source/blender/blenkernel/intern/pbvh_pixels.cc
@@ -71,7 +71,7 @@ static void extract_barycentric_pixels(UDIMTilePixels &tile_data,
int x;
for (x = minx; x < maxx; x++) {
- float2 uv(float(x) / image_buffer->x, float(y) / image_buffer->y);
+ float2 uv((float(x) + 0.5f) / image_buffer->x, (float(y) + 0.5f) / image_buffer->y);
float3 barycentric_weights;
barycentric_weights_v2(uvs[0], uvs[1], uvs[2], uv, barycentric_weights);
diff --git a/source/blender/blenkernel/intern/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_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/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_fileops.h b/source/blender/blenlib/BLI_fileops.h
index 3ce2b90e729..9a24b09fc66 100644
--- a/source/blender/blenlib/BLI_fileops.h
+++ b/source/blender/blenlib/BLI_fileops.h
@@ -178,7 +178,7 @@ void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries);
* Convert given entry's size into human-readable strings.
*/
void BLI_filelist_entry_size_to_string(const struct stat *st,
- uint64_t sz,
+ uint64_t st_size_fallback,
bool compact,
char r_size[FILELIST_DIRENTRY_SIZE_LEN]);
/**
diff --git a/source/blender/blenlib/BLI_float3x3.hh b/source/blender/blenlib/BLI_float3x3.hh
new file mode 100644
index 00000000000..62478556d9b
--- /dev/null
+++ b/source/blender/blenlib/BLI_float3x3.hh
@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include <cmath>
+#include <cstdint>
+
+#include "BLI_assert.h"
+#include "BLI_math_base.h"
+#include "BLI_math_matrix.h"
+#include "BLI_math_vec_types.hh"
+#include "BLI_math_vector.h"
+
+namespace blender {
+
+struct float3x3 {
+ /* A 3x3 matrix in column major order. */
+ float values[3][3];
+
+ float3x3() = default;
+
+ float3x3(const float *matrix)
+ {
+ memcpy(values, matrix, sizeof(float) * 3 * 3);
+ }
+
+ float3x3(const float matrix[3][3]) : float3x3(static_cast<const float *>(matrix[0]))
+ {
+ }
+
+ static float3x3 zero()
+ {
+ float3x3 result;
+ zero_m3(result.values);
+ return result;
+ }
+
+ static float3x3 identity()
+ {
+ float3x3 result;
+ unit_m3(result.values);
+ return result;
+ }
+
+ static float3x3 from_translation(const float2 translation)
+ {
+ float3x3 result = identity();
+ result.values[2][0] = translation.x;
+ result.values[2][1] = translation.y;
+ return result;
+ }
+
+ static float3x3 from_rotation(float rotation)
+ {
+ float3x3 result = zero();
+ const float cosine = std::cos(rotation);
+ const float sine = std::sin(rotation);
+ result.values[0][0] = cosine;
+ result.values[0][1] = sine;
+ result.values[1][0] = -sine;
+ result.values[1][1] = cosine;
+ result.values[2][2] = 1.0f;
+ return result;
+ }
+
+ static float3x3 from_translation_rotation_scale(const float2 translation,
+ float rotation,
+ const float2 scale)
+ {
+ float3x3 result;
+ const float cosine = std::cos(rotation);
+ const float sine = std::sin(rotation);
+ result.values[0][0] = scale.x * cosine;
+ result.values[0][1] = scale.x * sine;
+ result.values[0][2] = 0.0f;
+ result.values[1][0] = scale.y * -sine;
+ result.values[1][1] = scale.y * cosine;
+ result.values[1][2] = 0.0f;
+ result.values[2][0] = translation.x;
+ result.values[2][1] = translation.y;
+ result.values[2][2] = 1.0f;
+ return result;
+ }
+
+ static float3x3 from_normalized_axes(const float2 translation,
+ const float2 horizontal,
+ const float2 vertical)
+ {
+ BLI_ASSERT_UNIT_V2(horizontal);
+ BLI_ASSERT_UNIT_V2(vertical);
+
+ float3x3 result;
+ result.values[0][0] = horizontal.x;
+ result.values[0][1] = horizontal.y;
+ result.values[0][2] = 0.0f;
+ result.values[1][0] = vertical.x;
+ result.values[1][1] = vertical.y;
+ result.values[1][2] = 0.0f;
+ result.values[2][0] = translation.x;
+ result.values[2][1] = translation.y;
+ result.values[2][2] = 1.0f;
+ return result;
+ }
+
+ /* Construct a transformation that is pivoted around the given origin point. So for instance,
+ * from_origin_transformation(from_rotation(M_PI_2), float2(0.0f, 2.0f))
+ * will construct a transformation representing a 90 degree rotation around the point (0, 2). */
+ static float3x3 from_origin_transformation(const float3x3 &transformation, const float2 origin)
+ {
+ return from_translation(origin) * transformation * from_translation(-origin);
+ }
+
+ operator float *()
+ {
+ return &values[0][0];
+ }
+
+ operator const float *() const
+ {
+ return &values[0][0];
+ }
+
+ float *operator[](const int64_t index)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < 3);
+ return &values[index][0];
+ }
+
+ const float *operator[](const int64_t index) const
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < 3);
+ return &values[index][0];
+ }
+
+ using c_style_float3x3 = float[3][3];
+ c_style_float3x3 &ptr()
+ {
+ return values;
+ }
+
+ const c_style_float3x3 &ptr() const
+ {
+ return values;
+ }
+
+ friend float3x3 operator*(const float3x3 &a, const float3x3 &b)
+ {
+ float3x3 result;
+ mul_m3_m3m3(result.values, a.values, b.values);
+ return result;
+ }
+
+ void operator*=(const float3x3 &other)
+ {
+ mul_m3_m3_post(values, other.values);
+ }
+
+ friend float2 operator*(const float3x3 &transformation, const float2 &vector)
+ {
+ float2 result;
+ mul_v2_m3v2(result, transformation.values, vector);
+ return result;
+ }
+
+ friend float2 operator*(const float3x3 &transformation, const float (*vector)[2])
+ {
+ return transformation * float2(vector);
+ }
+
+ float3x3 transposed() const
+ {
+ float3x3 result;
+ transpose_m3_m3(result.values, values);
+ return result;
+ }
+
+ float3x3 inverted() const
+ {
+ float3x3 result;
+ invert_m3_m3(result.values, values);
+ return result;
+ }
+
+ friend bool operator==(const float3x3 &a, const float3x3 &b)
+ {
+ return equals_m3m3(a.values, b.values);
+ }
+};
+
+} // namespace blender
diff --git a/source/blender/blenlib/BLI_length_parameterize.hh b/source/blender/blenlib/BLI_length_parameterize.hh
index 6fe1c6513a2..f13641c3a65 100644
--- a/source/blender/blenlib/BLI_length_parameterize.hh
+++ b/source/blender/blenlib/BLI_length_parameterize.hh
@@ -17,7 +17,7 @@ namespace blender::length_parameterize {
* Return the size of the necessary lengths array for a group of points, taking into account the
* possible last cyclic segment.
*
- * \note This is the same as #bke::curves::curve_segment_size.
+ * \note This is the same as #bke::curves::curve_segment_num.
*/
inline int lengths_num(const int points_num, const bool cyclic)
{
diff --git a/source/blender/blenlib/BLI_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/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index e0f28522d6c..109230ebfa7 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -79,8 +79,8 @@ set(SRC
intern/kdtree_3d.c
intern/kdtree_4d.c
intern/lasso_2d.c
- intern/listbase.c
intern/length_parameterize.cc
+ intern/listbase.c
intern/math_base.c
intern/math_base_inline.c
intern/math_base_safe_inline.c
@@ -199,6 +199,7 @@ set(SRC
BLI_fileops.hh
BLI_fileops_types.h
BLI_filereader.h
+ BLI_float3x3.hh
BLI_float4x4.hh
BLI_fnmatch.h
BLI_function_ref.hh
@@ -274,8 +275,8 @@ set(SRC
BLI_multi_value_map.hh
BLI_noise.h
BLI_noise.hh
- BLI_path_util.h
BLI_parameter_pack_utils.hh
+ BLI_path_util.h
BLI_polyfill_2d.h
BLI_polyfill_2d_beautify.h
BLI_probing_strategies.hh
@@ -431,6 +432,7 @@ if(WITH_GTESTS)
tests/BLI_edgehash_test.cc
tests/BLI_expr_pylike_eval_test.cc
tests/BLI_fileops_test.cc
+ tests/BLI_float3x3_test.cc
tests/BLI_function_ref_test.cc
tests/BLI_generic_array_test.cc
tests/BLI_generic_span_test.cc
diff --git a/source/blender/blenlib/intern/BLI_filelist.c b/source/blender/blenlib/intern/BLI_filelist.c
index 76fc5b6342a..c6178ebb3a0 100644
--- a/source/blender/blenlib/intern/BLI_filelist.c
+++ b/source/blender/blenlib/intern/BLI_filelist.c
@@ -237,7 +237,7 @@ unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_
}
void BLI_filelist_entry_size_to_string(const struct stat *st,
- const uint64_t sz,
+ const uint64_t st_size_fallback,
/* Used to change MB -> M, etc. - is that really useful? */
const bool UNUSED(compact),
char r_size[FILELIST_DIRENTRY_SIZE_LEN])
@@ -247,7 +247,7 @@ void BLI_filelist_entry_size_to_string(const struct stat *st,
* will buy us some time until files get bigger than 4GB or until
* everyone starts using __USE_FILE_OFFSET64 or equivalent.
*/
- double size = (double)(st ? st->st_size : sz);
+ double size = (double)(st ? st->st_size : st_size_fallback);
#ifdef WIN32
BLI_str_format_byte_unit(r_size, size, false);
#else
diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c
index aeeaf47d813..07e9eaf0f42 100644
--- a/source/blender/blenlib/intern/math_color.c
+++ b/source/blender/blenlib/intern/math_color.c
@@ -238,7 +238,7 @@ void rgb_to_hsl(float r, float g, float b, float *r_h, float *r_s, float *r_l)
{
const float cmax = max_fff(r, g, b);
const float cmin = min_fff(r, g, b);
- float h, s, l = min_ff(1.0, (cmax + cmin) / 2.0f);
+ float h, s, l = min_ff(1.0f, (cmax + cmin) / 2.0f);
if (cmax == cmin) {
h = s = 0.0f; /* achromatic */
diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c
index 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/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/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 27890a908ab..16bad11629a 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -1477,14 +1477,14 @@ BlendThumbnail *BLO_thumbnail_from_file(const char *filepath)
const int width = fd_data[0];
const int height = fd_data[1];
if (BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) {
- const size_t sz = BLEN_THUMB_MEMSIZE(width, height);
- data = MEM_mallocN(sz, __func__);
+ const size_t data_size = BLEN_THUMB_MEMSIZE(width, height);
+ data = MEM_mallocN(data_size, __func__);
if (data) {
- BLI_assert((sz - sizeof(*data)) ==
+ BLI_assert((data_size - sizeof(*data)) ==
(BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*fd_data) * 2)));
data->width = width;
data->height = height;
- memcpy(data->rect, &fd_data[2], sz - sizeof(*data));
+ memcpy(data->rect, &fd_data[2], data_size - sizeof(*data));
}
}
}
@@ -2068,7 +2068,7 @@ static void direct_link_id_embedded_id(BlendDataReader *reader,
static int direct_link_id_restore_recalc_exceptions(const ID *id_current)
{
/* Exception for armature objects, where the pose has direct points to the
- * armature databolock. */
+ * armature data-block. */
if (GS(id_current->name) == ID_OB && ((Object *)id_current)->pose) {
return ID_RECALC_GEOMETRY;
}
@@ -3857,14 +3857,14 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
const int width = data[0];
const int height = data[1];
if (BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) {
- const size_t sz = BLEN_THUMB_MEMSIZE(width, height);
- bfd->main->blen_thumb = MEM_mallocN(sz, __func__);
+ const size_t data_size = BLEN_THUMB_MEMSIZE(width, height);
+ bfd->main->blen_thumb = MEM_mallocN(data_size, __func__);
- BLI_assert((sz - sizeof(*bfd->main->blen_thumb)) ==
+ BLI_assert((data_size - sizeof(*bfd->main->blen_thumb)) ==
(BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*data) * 2)));
bfd->main->blen_thumb->width = width;
bfd->main->blen_thumb->height = height;
- memcpy(bfd->main->blen_thumb->rect, &data[2], sz - sizeof(*bfd->main->blen_thumb));
+ memcpy(bfd->main->blen_thumb->rect, &data[2], data_size - sizeof(*bfd->main->blen_thumb));
}
}
}
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 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/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h
index 0246850123a..9d5737a5b71 100644
--- a/source/blender/bmesh/bmesh_class.h
+++ b/source/blender/bmesh/bmesh_class.h
@@ -265,8 +265,20 @@ typedef struct BMFace {
* (the length of #BMFace.l_first circular linked list).
*/
int len;
- float no[3]; /* face normal */
- short mat_nr; /* material index */
+ /**
+ * Face normal, see #BM_face_calc_normal.
+ */
+ float no[3];
+ /**
+ * Material index, typically >= 0 and < #Mesh.totcol although this isn't enforced
+ * Python for e.g. can set this to any positive value since scripts may create
+ * mesh data first and setup material slots later.
+ *
+ * When using to index into a material array it's range should be checked first,
+ * values exceeding the range should be ignored or treated as zero
+ * (if a material slot needs to be used - when drawing for e.g.)
+ */
+ short mat_nr;
// short _pad[3];
} BMFace;
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt
index 66f813e02b2..55e349423bb 100644
--- a/source/blender/compositor/CMakeLists.txt
+++ b/source/blender/compositor/CMakeLists.txt
@@ -259,12 +259,16 @@ set(SRC
# converter nodes
nodes/COM_CombineColorNode.cc
nodes/COM_CombineColorNode.h
+ nodes/COM_CombineColorNodeLegacy.cc
+ nodes/COM_CombineColorNodeLegacy.h
nodes/COM_CombineXYZNode.cc
nodes/COM_CombineXYZNode.h
nodes/COM_IDMaskNode.cc
nodes/COM_IDMaskNode.h
nodes/COM_SeparateColorNode.cc
nodes/COM_SeparateColorNode.h
+ nodes/COM_SeparateColorNodeLegacy.cc
+ nodes/COM_SeparateColorNodeLegacy.h
nodes/COM_SeparateXYZNode.cc
nodes/COM_SeparateXYZNode.h
diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc
index 13d412a338b..6d7341376e9 100644
--- a/source/blender/compositor/intern/COM_Converter.cc
+++ b/source/blender/compositor/intern/COM_Converter.cc
@@ -29,6 +29,7 @@
#include "COM_ColorSpillNode.h"
#include "COM_ColorToBWNode.h"
#include "COM_CombineColorNode.h"
+#include "COM_CombineColorNodeLegacy.h"
#include "COM_CombineXYZNode.h"
#include "COM_CompositorNode.h"
#include "COM_ConvertAlphaNode.h"
@@ -82,6 +83,7 @@
#include "COM_ScaleOperation.h"
#include "COM_SceneTimeNode.h"
#include "COM_SeparateColorNode.h"
+#include "COM_SeparateColorNodeLegacy.h"
#include "COM_SeparateXYZNode.h"
#include "COM_SetAlphaNode.h"
#include "COM_SetValueOperation.h"
@@ -169,28 +171,34 @@ Node *COM_convert_bnode(bNode *b_node)
case CMP_NODE_BRIGHTCONTRAST:
node = new BrightnessNode(b_node);
break;
- case CMP_NODE_SEPRGBA:
+ case CMP_NODE_SEPARATE_COLOR:
+ node = new SeparateColorNode(b_node);
+ break;
+ case CMP_NODE_COMBINE_COLOR:
+ node = new CombineColorNode(b_node);
+ break;
+ case CMP_NODE_SEPRGBA_LEGACY:
node = new SeparateRGBANode(b_node);
break;
- case CMP_NODE_COMBRGBA:
+ case CMP_NODE_COMBRGBA_LEGACY:
node = new CombineRGBANode(b_node);
break;
- case CMP_NODE_SEPHSVA:
+ case CMP_NODE_SEPHSVA_LEGACY:
node = new SeparateHSVANode(b_node);
break;
- case CMP_NODE_COMBHSVA:
+ case CMP_NODE_COMBHSVA_LEGACY:
node = new CombineHSVANode(b_node);
break;
- case CMP_NODE_SEPYUVA:
+ case CMP_NODE_SEPYUVA_LEGACY:
node = new SeparateYUVANode(b_node);
break;
- case CMP_NODE_COMBYUVA:
+ case CMP_NODE_COMBYUVA_LEGACY:
node = new CombineYUVANode(b_node);
break;
- case CMP_NODE_SEPYCCA:
+ case CMP_NODE_SEPYCCA_LEGACY:
node = new SeparateYCCANode(b_node);
break;
- case CMP_NODE_COMBYCCA:
+ case CMP_NODE_COMBYCCA_LEGACY:
node = new CombineYCCANode(b_node);
break;
case CMP_NODE_ALPHAOVER:
diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.cc b/source/blender/compositor/nodes/COM_CombineColorNode.cc
index 0c8748fd2d6..ca2c59478fd 100644
--- a/source/blender/compositor/nodes/COM_CombineColorNode.cc
+++ b/source/blender/compositor/nodes/COM_CombineColorNode.cc
@@ -12,7 +12,7 @@ CombineColorNode::CombineColorNode(bNode *editor_node) : Node(editor_node)
}
void CombineColorNode::convert_to_operations(NodeConverter &converter,
- const CompositorContext &context) const
+ const CompositorContext &UNUSED(context)) const
{
NodeInput *input_rsocket = this->get_input_socket(0);
NodeInput *input_gsocket = this->get_input_socket(1);
@@ -40,7 +40,39 @@ void CombineColorNode::convert_to_operations(NodeConverter &converter,
converter.map_input_socket(input_bsocket, operation->get_input_socket(2));
converter.map_input_socket(input_asocket, operation->get_input_socket(3));
- NodeOperation *color_conv = get_color_converter(context);
+ bNode *editor_node = this->get_bnode();
+ NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)editor_node->storage;
+
+ NodeOperation *color_conv = nullptr;
+ switch (storage->mode) {
+ case CMP_NODE_COMBSEP_COLOR_RGB: {
+ /* Pass */
+ break;
+ }
+ case CMP_NODE_COMBSEP_COLOR_HSV: {
+ color_conv = new ConvertHSVToRGBOperation();
+ break;
+ }
+ case CMP_NODE_COMBSEP_COLOR_HSL: {
+ color_conv = new ConvertHSLToRGBOperation();
+ break;
+ }
+ case CMP_NODE_COMBSEP_COLOR_YCC: {
+ ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation();
+ operation->set_mode(storage->ycc_mode);
+ color_conv = operation;
+ break;
+ }
+ case CMP_NODE_COMBSEP_COLOR_YUV: {
+ color_conv = new ConvertYUVToRGBOperation();
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+
if (color_conv) {
converter.add_operation(color_conv);
@@ -52,27 +84,4 @@ void CombineColorNode::convert_to_operations(NodeConverter &converter,
}
}
-NodeOperation *CombineRGBANode::get_color_converter(const CompositorContext & /*context*/) const
-{
- return nullptr; /* no conversion needed */
-}
-
-NodeOperation *CombineHSVANode::get_color_converter(const CompositorContext & /*context*/) const
-{
- return new ConvertHSVToRGBOperation();
-}
-
-NodeOperation *CombineYCCANode::get_color_converter(const CompositorContext & /*context*/) const
-{
- ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation();
- bNode *editor_node = this->get_bnode();
- operation->set_mode(editor_node->custom1);
- return operation;
-}
-
-NodeOperation *CombineYUVANode::get_color_converter(const CompositorContext & /*context*/) const
-{
- return new ConvertYUVToRGBOperation();
-}
-
} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.h b/source/blender/compositor/nodes/COM_CombineColorNode.h
index 2bead24cb02..7576cc9eb2d 100644
--- a/source/blender/compositor/nodes/COM_CombineColorNode.h
+++ b/source/blender/compositor/nodes/COM_CombineColorNode.h
@@ -12,45 +12,6 @@ class CombineColorNode : public Node {
CombineColorNode(bNode *editor_node);
void convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const override;
-
- protected:
- virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0;
-};
-
-class CombineRGBANode : public CombineColorNode {
- public:
- CombineRGBANode(bNode *editor_node) : CombineColorNode(editor_node)
- {
- }
-
- NodeOperation *get_color_converter(const CompositorContext &context) const override;
-};
-
-class CombineHSVANode : public CombineColorNode {
- public:
- CombineHSVANode(bNode *editor_node) : CombineColorNode(editor_node)
- {
- }
-
- NodeOperation *get_color_converter(const CompositorContext &context) const override;
-};
-
-class CombineYCCANode : public CombineColorNode {
- public:
- CombineYCCANode(bNode *editor_node) : CombineColorNode(editor_node)
- {
- }
-
- NodeOperation *get_color_converter(const CompositorContext &context) const override;
-};
-
-class CombineYUVANode : public CombineColorNode {
- public:
- CombineYUVANode(bNode *editor_node) : CombineColorNode(editor_node)
- {
- }
-
- NodeOperation *get_color_converter(const CompositorContext &context) const override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc
new file mode 100644
index 00000000000..d5ba379bfc2
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2011 Blender Foundation. */
+
+#include "COM_CombineColorNodeLegacy.h"
+
+#include "COM_ConvertOperation.h"
+
+namespace blender::compositor {
+
+CombineColorNodeLegacy::CombineColorNodeLegacy(bNode *editor_node) : Node(editor_node)
+{
+}
+
+void CombineColorNodeLegacy::convert_to_operations(NodeConverter &converter,
+ const CompositorContext &context) const
+{
+ NodeInput *input_rsocket = this->get_input_socket(0);
+ NodeInput *input_gsocket = this->get_input_socket(1);
+ NodeInput *input_bsocket = this->get_input_socket(2);
+ NodeInput *input_asocket = this->get_input_socket(3);
+ NodeOutput *output_socket = this->get_output_socket(0);
+
+ CombineChannelsOperation *operation = new CombineChannelsOperation();
+ if (input_rsocket->is_linked()) {
+ operation->set_canvas_input_index(0);
+ }
+ else if (input_gsocket->is_linked()) {
+ operation->set_canvas_input_index(1);
+ }
+ else if (input_bsocket->is_linked()) {
+ operation->set_canvas_input_index(2);
+ }
+ else {
+ operation->set_canvas_input_index(3);
+ }
+ converter.add_operation(operation);
+
+ converter.map_input_socket(input_rsocket, operation->get_input_socket(0));
+ converter.map_input_socket(input_gsocket, operation->get_input_socket(1));
+ converter.map_input_socket(input_bsocket, operation->get_input_socket(2));
+ converter.map_input_socket(input_asocket, operation->get_input_socket(3));
+
+ NodeOperation *color_conv = get_color_converter(context);
+ if (color_conv) {
+ converter.add_operation(color_conv);
+
+ converter.add_link(operation->get_output_socket(), color_conv->get_input_socket(0));
+ converter.map_output_socket(output_socket, color_conv->get_output_socket());
+ }
+ else {
+ converter.map_output_socket(output_socket, operation->get_output_socket());
+ }
+}
+
+NodeOperation *CombineRGBANode::get_color_converter(const CompositorContext & /*context*/) const
+{
+ return nullptr; /* no conversion needed */
+}
+
+NodeOperation *CombineHSVANode::get_color_converter(const CompositorContext & /*context*/) const
+{
+ return new ConvertHSVToRGBOperation();
+}
+
+NodeOperation *CombineYCCANode::get_color_converter(const CompositorContext & /*context*/) const
+{
+ ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation();
+ bNode *editor_node = this->get_bnode();
+ operation->set_mode(editor_node->custom1);
+ return operation;
+}
+
+NodeOperation *CombineYUVANode::get_color_converter(const CompositorContext & /*context*/) const
+{
+ return new ConvertYUVToRGBOperation();
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.h b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.h
new file mode 100644
index 00000000000..edc66c8932f
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2011 Blender Foundation. */
+
+#pragma once
+
+#include "COM_Node.h"
+
+namespace blender::compositor {
+
+class CombineColorNodeLegacy : public Node {
+ public:
+ CombineColorNodeLegacy(bNode *editor_node);
+ void convert_to_operations(NodeConverter &converter,
+ const CompositorContext &context) const override;
+
+ protected:
+ virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0;
+};
+
+class CombineRGBANode : public CombineColorNodeLegacy {
+ public:
+ CombineRGBANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node)
+ {
+ }
+
+ NodeOperation *get_color_converter(const CompositorContext &context) const override;
+};
+
+class CombineHSVANode : public CombineColorNodeLegacy {
+ public:
+ CombineHSVANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node)
+ {
+ }
+
+ NodeOperation *get_color_converter(const CompositorContext &context) const override;
+};
+
+class CombineYCCANode : public CombineColorNodeLegacy {
+ public:
+ CombineYCCANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node)
+ {
+ }
+
+ NodeOperation *get_color_converter(const CompositorContext &context) const override;
+};
+
+class CombineYUVANode : public CombineColorNodeLegacy {
+ public:
+ CombineYUVANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node)
+ {
+ }
+
+ NodeOperation *get_color_converter(const CompositorContext &context) const override;
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.cc b/source/blender/compositor/nodes/COM_SeparateColorNode.cc
index 221d80e67f2..a956c02ed42 100644
--- a/source/blender/compositor/nodes/COM_SeparateColorNode.cc
+++ b/source/blender/compositor/nodes/COM_SeparateColorNode.cc
@@ -12,7 +12,7 @@ SeparateColorNode::SeparateColorNode(bNode *editor_node) : Node(editor_node)
}
void SeparateColorNode::convert_to_operations(NodeConverter &converter,
- const CompositorContext &context) const
+ const CompositorContext &UNUSED(context)) const
{
NodeInput *image_socket = this->get_input_socket(0);
NodeOutput *output_rsocket = this->get_output_socket(0);
@@ -20,7 +20,39 @@ void SeparateColorNode::convert_to_operations(NodeConverter &converter,
NodeOutput *output_bsocket = this->get_output_socket(2);
NodeOutput *output_asocket = this->get_output_socket(3);
- NodeOperation *color_conv = get_color_converter(context);
+ bNode *editor_node = this->get_bnode();
+ NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)editor_node->storage;
+
+ NodeOperation *color_conv = nullptr;
+ switch (storage->mode) {
+ case CMP_NODE_COMBSEP_COLOR_RGB: {
+ /* Pass */
+ break;
+ }
+ case CMP_NODE_COMBSEP_COLOR_HSV: {
+ color_conv = new ConvertRGBToHSVOperation();
+ break;
+ }
+ case CMP_NODE_COMBSEP_COLOR_HSL: {
+ color_conv = new ConvertRGBToHSLOperation();
+ break;
+ }
+ case CMP_NODE_COMBSEP_COLOR_YCC: {
+ ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation();
+ operation->set_mode(storage->ycc_mode);
+ color_conv = operation;
+ break;
+ }
+ case CMP_NODE_COMBSEP_COLOR_YUV: {
+ color_conv = new ConvertRGBToYUVOperation();
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+
if (color_conv) {
converter.add_operation(color_conv);
@@ -84,27 +116,4 @@ void SeparateColorNode::convert_to_operations(NodeConverter &converter,
}
}
-NodeOperation *SeparateRGBANode::get_color_converter(const CompositorContext & /*context*/) const
-{
- return nullptr; /* no conversion needed */
-}
-
-NodeOperation *SeparateHSVANode::get_color_converter(const CompositorContext & /*context*/) const
-{
- return new ConvertRGBToHSVOperation();
-}
-
-NodeOperation *SeparateYCCANode::get_color_converter(const CompositorContext & /*context*/) const
-{
- ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation();
- bNode *editor_node = this->get_bnode();
- operation->set_mode(editor_node->custom1);
- return operation;
-}
-
-NodeOperation *SeparateYUVANode::get_color_converter(const CompositorContext & /*context*/) const
-{
- return new ConvertRGBToYUVOperation();
-}
-
} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.h b/source/blender/compositor/nodes/COM_SeparateColorNode.h
index 78ab0959f43..6adb2d0bb22 100644
--- a/source/blender/compositor/nodes/COM_SeparateColorNode.h
+++ b/source/blender/compositor/nodes/COM_SeparateColorNode.h
@@ -12,45 +12,6 @@ class SeparateColorNode : public Node {
SeparateColorNode(bNode *editor_node);
void convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const override;
-
- protected:
- virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0;
-};
-
-class SeparateRGBANode : public SeparateColorNode {
- public:
- SeparateRGBANode(bNode *editor_node) : SeparateColorNode(editor_node)
- {
- }
-
- NodeOperation *get_color_converter(const CompositorContext &context) const override;
-};
-
-class SeparateHSVANode : public SeparateColorNode {
- public:
- SeparateHSVANode(bNode *editor_node) : SeparateColorNode(editor_node)
- {
- }
-
- NodeOperation *get_color_converter(const CompositorContext &context) const override;
-};
-
-class SeparateYCCANode : public SeparateColorNode {
- public:
- SeparateYCCANode(bNode *editor_node) : SeparateColorNode(editor_node)
- {
- }
-
- NodeOperation *get_color_converter(const CompositorContext &context) const override;
-};
-
-class SeparateYUVANode : public SeparateColorNode {
- public:
- SeparateYUVANode(bNode *editor_node) : SeparateColorNode(editor_node)
- {
- }
-
- NodeOperation *get_color_converter(const CompositorContext &context) const override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc
new file mode 100644
index 00000000000..c3728bc152f
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2011 Blender Foundation. */
+
+#include "COM_SeparateColorNodeLegacy.h"
+
+#include "COM_ConvertOperation.h"
+
+namespace blender::compositor {
+
+SeparateColorNodeLegacy::SeparateColorNodeLegacy(bNode *editor_node) : Node(editor_node)
+{
+}
+
+void SeparateColorNodeLegacy::convert_to_operations(NodeConverter &converter,
+ const CompositorContext &context) const
+{
+ NodeInput *image_socket = this->get_input_socket(0);
+ NodeOutput *output_rsocket = this->get_output_socket(0);
+ NodeOutput *output_gsocket = this->get_output_socket(1);
+ NodeOutput *output_bsocket = this->get_output_socket(2);
+ NodeOutput *output_asocket = this->get_output_socket(3);
+
+ NodeOperation *color_conv = get_color_converter(context);
+ if (color_conv) {
+ converter.add_operation(color_conv);
+
+ converter.map_input_socket(image_socket, color_conv->get_input_socket(0));
+ }
+
+ {
+ SeparateChannelOperation *operation = new SeparateChannelOperation();
+ operation->set_channel(0);
+ converter.add_operation(operation);
+
+ if (color_conv) {
+ converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0));
+ }
+ else {
+ converter.map_input_socket(image_socket, operation->get_input_socket(0));
+ }
+ converter.map_output_socket(output_rsocket, operation->get_output_socket(0));
+ }
+
+ {
+ SeparateChannelOperation *operation = new SeparateChannelOperation();
+ operation->set_channel(1);
+ converter.add_operation(operation);
+
+ if (color_conv) {
+ converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0));
+ }
+ else {
+ converter.map_input_socket(image_socket, operation->get_input_socket(0));
+ }
+ converter.map_output_socket(output_gsocket, operation->get_output_socket(0));
+ }
+
+ {
+ SeparateChannelOperation *operation = new SeparateChannelOperation();
+ operation->set_channel(2);
+ converter.add_operation(operation);
+
+ if (color_conv) {
+ converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0));
+ }
+ else {
+ converter.map_input_socket(image_socket, operation->get_input_socket(0));
+ }
+ converter.map_output_socket(output_bsocket, operation->get_output_socket(0));
+ }
+
+ {
+ SeparateChannelOperation *operation = new SeparateChannelOperation();
+ operation->set_channel(3);
+ converter.add_operation(operation);
+
+ if (color_conv) {
+ converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0));
+ }
+ else {
+ converter.map_input_socket(image_socket, operation->get_input_socket(0));
+ }
+ converter.map_output_socket(output_asocket, operation->get_output_socket(0));
+ }
+}
+
+NodeOperation *SeparateRGBANode::get_color_converter(const CompositorContext & /*context*/) const
+{
+ return nullptr; /* no conversion needed */
+}
+
+NodeOperation *SeparateHSVANode::get_color_converter(const CompositorContext & /*context*/) const
+{
+ return new ConvertRGBToHSVOperation();
+}
+
+NodeOperation *SeparateYCCANode::get_color_converter(const CompositorContext & /*context*/) const
+{
+ ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation();
+ bNode *editor_node = this->get_bnode();
+ operation->set_mode(editor_node->custom1);
+ return operation;
+}
+
+NodeOperation *SeparateYUVANode::get_color_converter(const CompositorContext & /*context*/) const
+{
+ return new ConvertRGBToYUVOperation();
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.h b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.h
new file mode 100644
index 00000000000..10b33039c86
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2011 Blender Foundation. */
+
+#pragma once
+
+#include "COM_Node.h"
+
+namespace blender::compositor {
+
+class SeparateColorNodeLegacy : public Node {
+ public:
+ SeparateColorNodeLegacy(bNode *editor_node);
+ void convert_to_operations(NodeConverter &converter,
+ const CompositorContext &context) const override;
+
+ protected:
+ virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0;
+};
+
+class SeparateRGBANode : public SeparateColorNodeLegacy {
+ public:
+ SeparateRGBANode(bNode *editor_node) : SeparateColorNodeLegacy(editor_node)
+ {
+ }
+
+ NodeOperation *get_color_converter(const CompositorContext &context) const override;
+};
+
+class SeparateHSVANode : public SeparateColorNodeLegacy {
+ public:
+ SeparateHSVANode(bNode *editor_node) : SeparateColorNodeLegacy(editor_node)
+ {
+ }
+
+ NodeOperation *get_color_converter(const CompositorContext &context) const override;
+};
+
+class SeparateYCCANode : public SeparateColorNodeLegacy {
+ public:
+ SeparateYCCANode(bNode *editor_node) : SeparateColorNodeLegacy(editor_node)
+ {
+ }
+
+ NodeOperation *get_color_converter(const CompositorContext &context) const override;
+};
+
+class SeparateYUVANode : public SeparateColorNodeLegacy {
+ public:
+ SeparateYUVANode(bNode *editor_node) : SeparateColorNodeLegacy(editor_node)
+ {
+ }
+
+ NodeOperation *get_color_converter(const CompositorContext &context) const override;
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc
index 7579abf792a..24c0c577ac7 100644
--- a/source/blender/compositor/operations/COM_ConvertOperation.cc
+++ b/source/blender/compositor/operations/COM_ConvertOperation.cc
@@ -464,6 +464,68 @@ void ConvertHSVToRGBOperation::update_memory_buffer_partial(BuffersIterator<floa
}
}
+/* ******** RGB to HSL ******** */
+
+ConvertRGBToHSLOperation::ConvertRGBToHSLOperation() : ConvertBaseOperation()
+{
+ this->add_input_socket(DataType::Color);
+ this->add_output_socket(DataType::Color);
+}
+
+void ConvertRGBToHSLOperation::execute_pixel_sampled(float output[4],
+ float x,
+ float y,
+ PixelSampler sampler)
+{
+ float input_color[4];
+ input_operation_->read_sampled(input_color, x, y, sampler);
+ rgb_to_hsl_v(input_color, output);
+ output[3] = input_color[3];
+}
+
+void ConvertRGBToHSLOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float *in = it.in(0);
+ rgb_to_hsl_v(in, it.out);
+ it.out[3] = in[3];
+ }
+}
+
+/* ******** HSL to RGB ******** */
+
+ConvertHSLToRGBOperation::ConvertHSLToRGBOperation() : ConvertBaseOperation()
+{
+ this->add_input_socket(DataType::Color);
+ this->add_output_socket(DataType::Color);
+}
+
+void ConvertHSLToRGBOperation::execute_pixel_sampled(float output[4],
+ float x,
+ float y,
+ PixelSampler sampler)
+{
+ float input_color[4];
+ input_operation_->read_sampled(input_color, x, y, sampler);
+ hsl_to_rgb_v(input_color, output);
+ output[0] = max_ff(output[0], 0.0f);
+ output[1] = max_ff(output[1], 0.0f);
+ output[2] = max_ff(output[2], 0.0f);
+ output[3] = input_color[3];
+}
+
+void ConvertHSLToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
+{
+ for (; !it.is_end(); ++it) {
+ const float *in = it.in(0);
+ hsl_to_rgb_v(in, it.out);
+ it.out[0] = max_ff(it.out[0], 0.0f);
+ it.out[1] = max_ff(it.out[1], 0.0f);
+ it.out[2] = max_ff(it.out[2], 0.0f);
+ it.out[3] = in[3];
+ }
+}
+
/* ******** Premul to Straight ******** */
ConvertPremulToStraightOperation::ConvertPremulToStraightOperation() : ConvertBaseOperation()
diff --git a/source/blender/compositor/operations/COM_ConvertOperation.h b/source/blender/compositor/operations/COM_ConvertOperation.h
index e1904d61d46..16d1e2e6bb5 100644
--- a/source/blender/compositor/operations/COM_ConvertOperation.h
+++ b/source/blender/compositor/operations/COM_ConvertOperation.h
@@ -172,6 +172,26 @@ class ConvertHSVToRGBOperation : public ConvertBaseOperation {
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
+class ConvertRGBToHSLOperation : public ConvertBaseOperation {
+ public:
+ ConvertRGBToHSLOperation();
+
+ void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
+};
+
+class ConvertHSLToRGBOperation : public ConvertBaseOperation {
+ public:
+ ConvertHSLToRGBOperation();
+
+ void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ protected:
+ void update_memory_buffer_partial(BuffersIterator<float> &it) override;
+};
+
class ConvertPremulToStraightOperation : public ConvertBaseOperation {
public:
ConvertPremulToStraightOperation();
diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
index 573a740dac8..725751d15af 100644
--- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
@@ -112,7 +112,7 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src,
double *X, *Y, *W;
const unsigned int src_width = src->get_width();
const unsigned int src_height = src->get_height();
- unsigned int x, y, sz;
+ unsigned int x, y, src_dim_max;
unsigned int i;
float *buffer = src->get_buffer();
const uint8_t num_channels = src->get_num_channels();
@@ -202,10 +202,10 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src,
(void)0
/* Intermediate buffers. */
- sz = MAX2(src_width, src_height);
- X = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss X buf");
- Y = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss Y buf");
- W = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss W buf");
+ src_dim_max = MAX2(src_width, src_height);
+ X = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss X buf");
+ Y = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss Y buf");
+ W = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss W buf");
if (xy & 1) { /* H. */
int offset;
for (y = 0; y < src_height; y++) {
diff --git a/source/blender/depsgraph/DEG_depsgraph_query.h b/source/blender/depsgraph/DEG_depsgraph_query.h
index ade75fa2f6f..12663e74d24 100644
--- a/source/blender/depsgraph/DEG_depsgraph_query.h
+++ b/source/blender/depsgraph/DEG_depsgraph_query.h
@@ -64,7 +64,7 @@ bool DEG_id_type_any_updated(const struct Depsgraph *depsgraph);
bool DEG_id_type_any_exists(const struct Depsgraph *depsgraph, short id_type);
/** Get additional evaluation flags for the given ID. */
-uint32_t DEG_get_eval_flags_for_id(const struct Depsgraph *graph, struct ID *id);
+uint32_t DEG_get_eval_flags_for_id(const struct Depsgraph *graph, const struct ID *id);
/** Get additional mesh CustomData_MeshMasks flags for the given object. */
void DEG_get_customdata_mask_for_object(const struct Depsgraph *graph,
diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc
index fd569599b8b..6ffc711a475 100644
--- a/source/blender/depsgraph/intern/depsgraph_query.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query.cc
@@ -32,6 +32,49 @@
#include "intern/eval/deg_eval_copy_on_write.h"
#include "intern/node/deg_node_id.h"
+namespace blender::deg {
+
+static const ID *get_original_id(const ID *id)
+{
+ if (id == nullptr) {
+ return nullptr;
+ }
+ if (id->orig_id == nullptr) {
+ return id;
+ }
+ BLI_assert((id->tag & LIB_TAG_COPIED_ON_WRITE) != 0);
+ return (ID *)id->orig_id;
+}
+
+static ID *get_original_id(ID *id)
+{
+ const ID *const_id = id;
+ return const_cast<ID *>(get_original_id(const_id));
+}
+
+static const ID *get_evaluated_id(const Depsgraph *deg_graph, const ID *id)
+{
+ if (id == nullptr) {
+ return nullptr;
+ }
+ /* TODO(sergey): This is a duplicate of Depsgraph::get_cow_id(),
+ * but here we never do assert, since we don't know nature of the
+ * incoming ID data-block. */
+ const IDNode *id_node = deg_graph->find_id_node(id);
+ if (id_node == nullptr) {
+ return id;
+ }
+ return id_node->id_cow;
+}
+
+static ID *get_evaluated_id(const Depsgraph *deg_graph, ID *id)
+{
+ const ID *const_id = id;
+ return const_cast<ID *>(get_evaluated_id(deg_graph, const_id));
+}
+
+} // namespace blender::deg
+
namespace deg = blender::deg;
struct Scene *DEG_get_input_scene(const Depsgraph *graph)
@@ -90,7 +133,7 @@ bool DEG_id_type_any_exists(const Depsgraph *depsgraph, short id_type)
return deg_graph->id_type_exist[BKE_idtype_idcode_to_index(id_type)] != 0;
}
-uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, ID *id)
+uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, const ID *id)
{
if (graph == nullptr) {
/* Happens when converting objects to mesh from a python script
@@ -102,7 +145,7 @@ uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, ID *id)
}
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
- const deg::IDNode *id_node = deg_graph->find_id_node(DEG_get_original_id(id));
+ const deg::IDNode *id_node = deg_graph->find_id_node(deg::get_original_id(id));
if (id_node == nullptr) {
/* TODO(sergey): Does it mean we need to check set scene? */
return 0;
@@ -171,18 +214,7 @@ Object *DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
ID *DEG_get_evaluated_id(const Depsgraph *depsgraph, ID *id)
{
- if (id == nullptr) {
- return nullptr;
- }
- /* TODO(sergey): This is a duplicate of Depsgraph::get_cow_id(),
- * but here we never do assert, since we don't know nature of the
- * incoming ID data-block. */
- const deg::Depsgraph *deg_graph = (const deg::Depsgraph *)depsgraph;
- const deg::IDNode *id_node = deg_graph->find_id_node(id);
- if (id_node == nullptr) {
- return id;
- }
- return id_node->id_cow;
+ return deg::get_evaluated_id(reinterpret_cast<const deg::Depsgraph *>(depsgraph), id);
}
void DEG_get_evaluated_rna_pointer(const Depsgraph *depsgraph,
@@ -249,14 +281,7 @@ Object *DEG_get_original_object(Object *object)
ID *DEG_get_original_id(ID *id)
{
- if (id == nullptr) {
- return nullptr;
- }
- if (id->orig_id == nullptr) {
- return id;
- }
- BLI_assert((id->tag & LIB_TAG_COPIED_ON_WRITE) != 0);
- return (ID *)id->orig_id;
+ return deg::get_original_id(id);
}
bool DEG_is_original_id(const ID *id)
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 1ff7585165b..f16e9e77dad 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -422,8 +422,8 @@ set(GLSL_SRC
intern/shaders/common_subdiv_vbo_lnor_comp.glsl
intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl
- intern/draw_shader_shared.h
intern/draw_common_shader_shared.h
+ intern/draw_shader_shared.h
engines/gpencil/shaders/gpencil_frag.glsl
engines/gpencil/shaders/gpencil_vert.glsl
diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
index 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..0b9909a904b 100644
--- a/source/blender/draw/engines/eevee/eevee_lightcache.c
+++ b/source/blender/draw/engines/eevee/eevee_lightcache.c
@@ -95,7 +95,7 @@ typedef struct EEVEE_LightBake {
/** Target layer to store the data to. */
int layer;
/** Sample count for the convolution. */
- float samples_ct, invsamples_ct;
+ float samples_count, invsamples_count;
/** Sampling bias during convolution step. */
float lod_factor;
/** Max cube-map LOD to sample when convolving. */
@@ -282,14 +282,14 @@ static void irradiance_pool_size_get(int visibility_size, int total_samples, int
(visibility_size / IRRADIANCE_SAMPLE_SIZE_Y);
/* The irradiance itself take one layer, hence the +1 */
- int layer_ct = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER);
+ int layer_count = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER);
- int texel_ct = (int)ceilf((float)total_samples / (float)(layer_ct - 1));
+ int texel_count = (int)ceilf((float)total_samples / (float)(layer_count - 1));
r_size[0] = visibility_size *
- max_ii(1, min_ii(texel_ct, (IRRADIANCE_MAX_POOL_SIZE / visibility_size)));
+ max_ii(1, min_ii(texel_count, (IRRADIANCE_MAX_POOL_SIZE / visibility_size)));
r_size[1] = visibility_size *
- max_ii(1, (texel_ct / (IRRADIANCE_MAX_POOL_SIZE / visibility_size)));
- r_size[2] = layer_ct;
+ max_ii(1, (texel_count / (IRRADIANCE_MAX_POOL_SIZE / visibility_size)));
+ r_size[2] = layer_count;
}
static bool EEVEE_lightcache_validate(const LightCache *light_cache,
diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c
index 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_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh
index 35eb33671db..7141928a20d 100644
--- a/source/blender/draw/engines/eevee_next/eevee_defines.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh
@@ -11,9 +11,11 @@
#pragma once
-/* Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536. */
-/* Current limiting factor is the sorting phase which is single pass and only sort within a
- * threadgroup which maximum size is 1024. */
+/**
+ Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536.
+ * Current limiting factor is the sorting phase which is single pass and only sort within a
+ * thread-group which maximum size is 1024.
+ */
#define CULLING_BATCH_SIZE 1024
/**
diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc
index 922f6c9e1ae..8fef1cf7fc5 100644
--- a/source/blender/draw/engines/eevee_next/eevee_instance.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc
@@ -21,9 +21,9 @@
namespace blender::eevee {
/* -------------------------------------------------------------------- */
-/** \name Init
+/** \name Initialization
*
- * Init funcions need to be called once at the start of a frame.
+ * Initialization functions need to be called once at the start of a frame.
* Active camera, render extent and enabled render passes are immutable until next init.
* This takes care of resizing output buffers and view in case a parameter changed.
* IMPORTANT: xxx.init() functions are NOT meant to acquire and allocate DRW resources.
diff --git a/source/blender/draw/engines/eevee_next/eevee_material.hh b/source/blender/draw/engines/eevee_next/eevee_material.hh
index 56f9b077f7a..af9ff6bf6ba 100644
--- a/source/blender/draw/engines/eevee_next/eevee_material.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_material.hh
@@ -104,7 +104,7 @@ static inline eMaterialGeometry to_material_geometry(const Object *ob)
}
}
-/** Unique key to identify each material in the hashmap. */
+/** Unique key to identify each material in the hash-map. */
struct MaterialKey {
Material *mat;
uint64_t options;
@@ -169,7 +169,7 @@ struct ShaderKey {
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Default Material Nodetree
+/** \name Default Material Node-Tree
*
* In order to support materials without nodetree we reuse and configure a standalone nodetree that
* we pass for shader generation. The GPUMaterial is still stored inside the Material even if
@@ -254,4 +254,4 @@ class MaterialModule {
/** \} */
-} // namespace blender::eevee \ No newline at end of file
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc
index 086c5f9f358..39c5cf6a536 100644
--- a/source/blender/draw/engines/eevee_next/eevee_shader.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc
@@ -25,7 +25,7 @@ ShaderModule *ShaderModule::g_shader_module = nullptr;
ShaderModule *ShaderModule::module_get()
{
if (g_shader_module == nullptr) {
- /* TODO(fclem) threadsafety. */
+ /* TODO(@fclem) thread-safety. */
g_shader_module = new ShaderModule();
}
return g_shader_module;
@@ -34,7 +34,7 @@ ShaderModule *ShaderModule::module_get()
void ShaderModule::module_free()
{
if (g_shader_module != nullptr) {
- /* TODO(fclem) threadsafety. */
+ /* TODO(@fclem) thread-safety. */
delete g_shader_module;
g_shader_module = nullptr;
}
@@ -148,7 +148,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
/** Noop. */
break;
case MAT_GEOM_CURVES:
- /** Hair attributes comme from sampler buffer. Transfer attributes to sampler. */
+ /** Hair attributes come from sampler buffer. Transfer attributes to sampler. */
for (auto &input : info.vertex_inputs_) {
if (input.name == "orco") {
/** NOTE: Orco is generated from strand position for now. */
@@ -163,14 +163,14 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
case MAT_GEOM_WORLD:
/**
* Only orco layer is supported by world and it is procedurally generated. These are here to
- * make the attribs_load function calls valids.
+ * make the attribs_load function calls valid.
*/
ATTR_FALLTHROUGH;
case MAT_GEOM_GPENCIL:
/**
* Only one uv and one color attribute layer are supported by gpencil objects and they are
* already declared in another createInfo. These are here to make the attribs_load
- * function calls valids.
+ * function calls valid.
*/
for (auto &input : info.vertex_inputs_) {
global_vars << input.type << " " << input.name << ";\n";
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh
index ba7c97b3b6a..29fcbafb167 100644
--- a/source/blender/draw/engines/eevee_next/eevee_shader.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh
@@ -36,7 +36,7 @@ class ShaderModule {
private:
std::array<GPUShader *, MAX_SHADER_TYPE> shaders_;
- /** Shared shader module accross all engine instances. */
+ /** Shared shader module across all engine instances. */
static ShaderModule *g_shader_module;
public:
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh
index 2225ccac43a..45b067d203e 100644
--- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh
@@ -27,7 +27,7 @@ using draw::TextureFromPool;
#define UBO_MIN_MAX_SUPPORTED_SIZE 1 << 14
/* -------------------------------------------------------------------- */
-/** \name Raytracing
+/** \name Ray-Tracing
* \{ */
enum eClosureBits : uint32_t {
diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.hh b/source/blender/draw/engines/eevee_next/eevee_sync.hh
index 51e0f86fe5c..34357193d3e 100644
--- a/source/blender/draw/engines/eevee_next/eevee_sync.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_sync.hh
@@ -27,7 +27,7 @@ class Instance;
/* -------------------------------------------------------------------- */
/** \name ObjectKey
*
- * Unique key to identify each object in the hashmap.
+ * Unique key to identify each object in the hash-map.
* \{ */
struct ObjectKey {
diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc
index df45200c712..de7341f814b 100644
--- a/source/blender/draw/engines/eevee_next/eevee_view.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_view.cc
@@ -9,7 +9,7 @@
* - The entire main view.
* - A fragment of the main view (for panoramic projections).
* - A shadow map view.
- * - A lightprobe view (either planar, cubemap, irradiance grid).
+ * - A light-probe view (either planar, cube-map, irradiance grid).
*
* A pass is a container for scene data. It is view agnostic but has specific logic depending on
* its type. Passes are shared between views.
@@ -40,7 +40,7 @@ void ShadingView::sync(int2 render_extent_)
int64_t render_pixel_count = render_extent_.x * (int64_t)render_extent_.y;
/* Divide pixel count between the 6 views. Rendering to a square target. */
extent_[0] = extent_[1] = ceilf(sqrtf(1 + (render_pixel_count / 6)));
- /* TODO(fclem) Clip unused views heres. */
+ /* TODO(@fclem): Clip unused views here. */
is_enabled_ = true;
}
else {
@@ -60,8 +60,8 @@ void ShadingView::sync(int2 render_extent_)
const float(*viewmat_p)[4] = viewmat.ptr(), (*winmat_p)[4] = winmat.ptr();
#if 0
if (false /* inst_.camera.is_panoramic() */) {
- /* TODO(fclem) Overscans. */
- /* For now a mandatory 5% overscan for DoF. */
+ /* TODO(@fclem) Over-scans. */
+ /* For now a mandatory 5% over-scan for DoF. */
float side = data.clip_near * 1.05f;
float near = data.clip_near;
float far = data.clip_far;
@@ -205,4 +205,4 @@ void ShadingView::update_view()
/** \} */
-} // namespace blender::eevee \ No newline at end of file
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_view.hh b/source/blender/draw/engines/eevee_next/eevee_view.hh
index ab7b5722de1..e78a3222d8b 100644
--- a/source/blender/draw/engines/eevee_next/eevee_view.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_view.hh
@@ -8,7 +8,7 @@
* A view is either:
* - The entire main view.
* - A portion of the main view (for panoramic projections).
- * - A lightprobe view (either planar, cubemap, irradiance grid).
+ * - A light-probe view (either planar, cube-map, irradiance grid).
*
* A pass is a container for scene data. It is view agnostic but has specific logic depending on
* its type. Passes are shared between views.
@@ -154,4 +154,4 @@ class MainView {
/** \} */
-} // namespace blender::eevee \ No newline at end of file
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_world.hh b/source/blender/draw/engines/eevee_next/eevee_world.hh
index 56554051eea..05177928436 100644
--- a/source/blender/draw/engines/eevee_next/eevee_world.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_world.hh
@@ -18,7 +18,7 @@ namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
-/** \name Default World Nodetree
+/** \name Default World Node-Tree
*
* In order to support worlds without nodetree we reuse and configure a standalone nodetree that
* we pass for shader generation. The GPUMaterial is still stored inside the World even if
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
index 4d6895bcde0..ccd67360073 100644
--- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
@@ -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_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/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/workbench_render.c b/source/blender/draw/engines/workbench/workbench_render.c
index 1279682e899..e5dcf6c5624 100644
--- a/source/blender/draw/engines/workbench/workbench_render.c
+++ b/source/blender/draw/engines/workbench/workbench_render.c
@@ -115,11 +115,11 @@ static void workbench_render_result_z(struct RenderLayer *rl,
float winmat[4][4];
DRW_view_winmat_get(NULL, winmat, false);
- int pix_ct = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect);
+ int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect);
/* Convert ogl depth [0..1] to view Z [near..far] */
if (DRW_view_is_persp_get(NULL)) {
- for (int i = 0; i < pix_ct; i++) {
+ for (int i = 0; i < pix_num; i++) {
if (rp->rect[i] == 1.0f) {
rp->rect[i] = 1e10f; /* Background */
}
@@ -135,7 +135,7 @@ static void workbench_render_result_z(struct RenderLayer *rl,
float far = DRW_view_far_distance_get(NULL);
float range = fabsf(far - near);
- for (int i = 0; i < pix_ct; i++) {
+ for (int i = 0; i < pix_num; i++) {
if (rp->rect[i] == 1.0f) {
rp->rect[i] = 1e10f; /* Background */
}
diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c
index 2c902e9b627..ce7773e7439 100644
--- a/source/blender/draw/engines/workbench/workbench_volume.c
+++ b/source/blender/draw/engines/workbench/workbench_volume.c
@@ -124,12 +124,12 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata,
double noise_ofs;
BLI_halton_1d(3, 0.0, wpd->taa_sample, &noise_ofs);
float dim[3], step_length, max_slice;
- float slice_ct[3] = {fds->res[0], fds->res[1], fds->res[2]};
- mul_v3_fl(slice_ct, max_ff(0.001f, fds->slice_per_voxel));
- max_slice = max_fff(slice_ct[0], slice_ct[1], slice_ct[2]);
+ float slice_count[3] = {fds->res[0], fds->res[1], fds->res[2]};
+ mul_v3_fl(slice_count, max_ff(0.001f, fds->slice_per_voxel));
+ max_slice = max_fff(slice_count[0], slice_count[1], slice_count[2]);
BKE_object_dimensions_get(ob, dim);
- invert_v3(slice_ct);
- mul_v3_v3(dim, slice_ct);
+ invert_v3(slice_count);
+ mul_v3_v3(dim, slice_count);
step_length = len_v3(dim);
grp = DRW_shgroup_create(sh, vedata->psl->volume_ps);
@@ -273,12 +273,12 @@ static void workbench_volume_object_cache_populate(WORKBENCH_Data *vedata,
float step_length, max_slice;
int resolution[3];
GPU_texture_get_mipmap_size(grid->texture, 0, resolution);
- float slice_ct[3] = {resolution[0], resolution[1], resolution[2]};
- mul_v3_fl(slice_ct, max_ff(0.001f, 5.0f));
- max_slice = max_fff(slice_ct[0], slice_ct[1], slice_ct[2]);
- invert_v3(slice_ct);
- mul_v3_v3(slice_ct, world_size);
- step_length = len_v3(slice_ct);
+ float slice_count[3] = {resolution[0], resolution[1], resolution[2]};
+ mul_v3_fl(slice_count, max_ff(0.001f, 5.0f));
+ max_slice = max_fff(slice_count[0], slice_count[1], slice_count[2]);
+ invert_v3(slice_count);
+ mul_v3_v3(slice_count, world_size);
+ step_length = len_v3(slice_count);
/* Set uniforms. */
grp = DRW_shgroup_create(sh, vedata->psl->volume_ps);
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index 712118e8282..572b87282e9 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -430,12 +430,12 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup,
DRW_shgroup_call_ex(shgroup, ob, NULL, geom, true, NULL)
void DRW_shgroup_call_range(
- DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint v_sta, uint v_ct);
+ DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint v_sta, uint v_num);
/**
* A count of 0 instance will use the default number of instance in the batch.
*/
void DRW_shgroup_call_instance_range(
- DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct);
+ DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_num);
void DRW_shgroup_call_compute(DRWShadingGroup *shgroup,
int groups_x_len,
diff --git a/source/blender/draw/intern/draw_cache_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_curves.cc b/source/blender/draw/intern/draw_curves.cc
index 88118361115..2edf596ac63 100644
--- a/source/blender/draw/intern/draw_curves.cc
+++ b/source/blender/draw/intern/draw_curves.cc
@@ -209,7 +209,7 @@ DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object,
DRW_shgroup_uniform_texture(shgrp, "ac", g_dummy_texture);
/* TODO: Generalize radius implementation for curves data type. */
- float hair_rad_shape = 1.0f;
+ float hair_rad_shape = 0.0f;
float hair_rad_root = 0.005f;
float hair_rad_tip = 0.0f;
bool hair_close_tip = true;
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index 4bbcf6eaf42..f71d88342f1 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -529,7 +529,7 @@ static void drw_manager_init(DRWManager *dst, GPUViewport *viewport, const int s
dst->view_data_active = dst->vmempool->view_data[view];
dst->resource_handle = 0;
dst->pass_handle = 0;
- dst->primary_view_ct = 0;
+ dst->primary_view_num = 0;
drw_viewport_data_reset(dst->vmempool);
diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h
index 7a9585262ff..2d0837370b2 100644
--- a/source/blender/draw/intern/draw_manager.h
+++ b/source/blender/draw/intern/draw_manager.h
@@ -617,7 +617,7 @@ typedef struct DRWManager {
DRWView *view_default;
DRWView *view_active;
DRWView *view_previous;
- uint primary_view_ct;
+ uint primary_view_num;
/** TODO(@fclem): Remove this. Only here to support
* shaders without common_view_lib.glsl */
ViewInfos view_storage_cpy;
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index b5432da0957..b0d8017940f 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -937,25 +937,25 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup,
}
void DRW_shgroup_call_range(
- DRWShadingGroup *shgroup, struct Object *ob, GPUBatch *geom, uint v_sta, uint v_ct)
+ DRWShadingGroup *shgroup, struct Object *ob, GPUBatch *geom, uint v_sta, uint v_num)
{
BLI_assert(geom != NULL);
if (G.f & G_FLAG_PICKSEL) {
drw_command_set_select_id(shgroup, NULL, DST.select_id);
}
DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob);
- drw_command_draw_range(shgroup, geom, handle, v_sta, v_ct);
+ drw_command_draw_range(shgroup, geom, handle, v_sta, v_num);
}
void DRW_shgroup_call_instance_range(
- DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct)
+ DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_num)
{
BLI_assert(geom != NULL);
if (G.f & G_FLAG_PICKSEL) {
drw_command_set_select_id(shgroup, NULL, DST.select_id);
}
DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob);
- drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_ct);
+ drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_num);
}
void DRW_shgroup_call_compute(DRWShadingGroup *shgroup,
@@ -1905,8 +1905,8 @@ DRWView *DRW_view_create(const float viewmat[4][4],
{
DRWView *view = BLI_memblock_alloc(DST.vmempool->views);
- if (DST.primary_view_ct < MAX_CULLED_VIEWS) {
- view->culling_mask = 1u << DST.primary_view_ct++;
+ if (DST.primary_view_num < MAX_CULLED_VIEWS) {
+ view->culling_mask = 1u << DST.primary_view_num++;
}
else {
BLI_assert(0);
diff --git a/source/blender/draw/intern/draw_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_vbo_select_idx.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc
index 2a4a6a186be..f4c54b2f881 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc
@@ -188,7 +188,7 @@ static void extract_vert_idx_init_subdiv(const DRWSubdivCache *subdiv_cache,
{
GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf);
const DRWSubdivLooseGeom &loose_geom = subdiv_cache->loose_geom;
- /* Each element points to an element in the ibo.points. */
+ /* Each element points to an element in the `ibo.points`. */
draw_subdiv_init_origindex_buffer(
vbo,
(int32_t *)GPU_vertbuf_get_data(subdiv_cache->verts_orig_index),
diff --git a/source/blender/draw/intern/shaders/common_globals_lib.glsl b/source/blender/draw/intern/shaders/common_globals_lib.glsl
index 0460ba56e4c..a8931292064 100644
--- a/source/blender/draw/intern/shaders/common_globals_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_globals_lib.glsl
@@ -110,7 +110,7 @@ layout(std140) uniform globalsBlock
vec4 screenVecs[2];
vec4 sizeViewport; /* Inverted size in zw. */
- float sizePixel; /* This one is for dpi scaling */
+ float sizePixel; /* This one is for DPI scaling. */
float pixelFac; /* To use with mul_project_m4_v3_zfac() */
float sizeObjectCenter;
float sizeLightCenter;
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index 7bb1d652bf1..c2d517588b2 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -4020,6 +4020,8 @@ static bool acf_nlaaction_setting_valid(bAnimContext *UNUSED(ac),
else {
return false;
}
+ case ACHANNEL_SETTING_SELECT: /* selected */
+ return true;
/* unsupported */
default:
@@ -4040,6 +4042,9 @@ static int acf_nlaaction_setting_flag(bAnimContext *UNUSED(ac),
*neg = true; /* XXX */
return ADT_NLA_EDIT_NOMAP;
+ case ACHANNEL_SETTING_SELECT: /* selected */
+ return ADT_UI_SELECTED;
+
default: /* unsupported */
return 0;
}
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index 31d90c8bfec..f148bb5b77d 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -125,6 +125,7 @@ void ANIM_set_active_channel(bAnimContext *ac,
case ANIMTYPE_DSHAIR:
case ANIMTYPE_DSPOINTCLOUD:
case ANIMTYPE_DSVOLUME:
+ case ANIMTYPE_NLAACTION:
case ANIMTYPE_DSSIMULATION: {
/* need to verify that this data is valid for now */
if (ale->adt) {
@@ -182,6 +183,7 @@ void ANIM_set_active_channel(bAnimContext *ac,
case ANIMTYPE_DSHAIR:
case ANIMTYPE_DSPOINTCLOUD:
case ANIMTYPE_DSVOLUME:
+ case ANIMTYPE_NLAACTION:
case ANIMTYPE_DSSIMULATION: {
/* need to verify that this data is valid for now */
if (ale && ale->adt) {
@@ -199,7 +201,6 @@ void ANIM_set_active_channel(bAnimContext *ac,
/* unhandled currently, but may be interesting */
case ANIMTYPE_MASKLAYER:
case ANIMTYPE_SHAPEKEY:
- case ANIMTYPE_NLAACTION:
break;
/* other types */
@@ -312,6 +313,7 @@ static eAnimChannels_SetFlag anim_channels_selection_flag_for_toggle(const ListB
case ANIMTYPE_DSHAIR:
case ANIMTYPE_DSPOINTCLOUD:
case ANIMTYPE_DSVOLUME:
+ case ANIMTYPE_NLAACTION:
case ANIMTYPE_DSSIMULATION: {
if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED)) {
return ACHANNEL_SETFLAG_CLEAR;
@@ -420,6 +422,7 @@ static void anim_channels_select_set(bAnimContext *ac,
case ANIMTYPE_DSHAIR:
case ANIMTYPE_DSPOINTCLOUD:
case ANIMTYPE_DSVOLUME:
+ case ANIMTYPE_NLAACTION:
case ANIMTYPE_DSSIMULATION: {
/* need to verify that this data is valid for now */
if (ale->adt) {
diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c
index 58d093c678d..786204a52ed 100644
--- a/source/blender/editors/animation/keyframes_draw.c
+++ b/source/blender/editors/animation/keyframes_draw.c
@@ -168,11 +168,11 @@ void draw_keyframe_shape(float x,
/* Common attributes shared between the draw calls. */
typedef struct DrawKeylistUIData {
float alpha;
- float icon_sz;
- float half_icon_sz;
- float smaller_sz;
- float ipo_sz;
- float gpencil_sz;
+ float icon_size;
+ float half_icon_size;
+ float smaller_size;
+ float ipo_size;
+ float gpencil_size;
float screenspace_margin;
float sel_color[4];
float unsel_color[4];
@@ -195,11 +195,11 @@ static void draw_keylist_ui_data_init(DrawKeylistUIData *ctx,
/* TODO: allow this opacity factor to be themed? */
ctx->alpha = channel_locked ? 0.25f : 1.0f;
- ctx->icon_sz = U.widget_unit * 0.5f * yscale_fac;
- ctx->half_icon_sz = 0.5f * ctx->icon_sz;
- ctx->smaller_sz = 0.35f * ctx->icon_sz;
- ctx->ipo_sz = 0.1f * ctx->icon_sz;
- ctx->gpencil_sz = ctx->smaller_sz * 0.8f;
+ ctx->icon_size = U.widget_unit * 0.5f * yscale_fac;
+ ctx->half_icon_size = 0.5f * ctx->icon_size;
+ ctx->smaller_size = 0.35f * ctx->icon_size;
+ ctx->ipo_size = 0.1f * ctx->icon_size;
+ ctx->gpencil_size = ctx->smaller_size * 0.8f;
ctx->screenspace_margin = (0.35f * (float)UI_UNIT_X) / UI_view2d_scale_get_x(v2d);
ctx->show_ipo = (saction_flag & SACTION_SHOW_INTERPOLATION) != 0;
@@ -242,8 +242,8 @@ static void draw_keylist_block_gpencil(const DrawKeylistUIData *ctx,
&(const rctf){
.xmin = ab->cfra,
.xmax = min_ff(ab->next->cfra - (ctx->screenspace_margin * size), ab->next->cfra),
- .ymin = ypos - ctx->gpencil_sz,
- .ymax = ypos + ctx->gpencil_sz,
+ .ymin = ypos - ctx->gpencil_size,
+ .ymax = ypos + ctx->gpencil_size,
},
true,
0.25f * (float)UI_UNIT_X,
@@ -259,8 +259,8 @@ static void draw_keylist_block_moving_hold(const DrawKeylistUIData *ctx,
&(const rctf){
.xmin = ab->cfra,
.xmax = ab->next->cfra,
- .ymin = ypos - ctx->smaller_sz,
- .ymax = ypos + ctx->smaller_sz,
+ .ymin = ypos - ctx->smaller_size,
+ .ymax = ypos + ctx->smaller_size,
},
true,
3.0f,
@@ -275,8 +275,8 @@ static void draw_keylist_block_standard(const DrawKeylistUIData *ctx,
&(const rctf){
.xmin = ab->cfra,
.xmax = ab->next->cfra,
- .ymin = ypos - ctx->half_icon_sz,
- .ymax = ypos + ctx->half_icon_sz,
+ .ymin = ypos - ctx->half_icon_size,
+ .ymax = ypos + ctx->half_icon_size,
},
true,
3.0f,
@@ -291,8 +291,8 @@ static void draw_keylist_block_interpolation_line(const DrawKeylistUIData *ctx,
&(const rctf){
.xmin = ab->cfra,
.xmax = ab->next->cfra,
- .ymin = ypos - ctx->ipo_sz,
- .ymax = ypos + ctx->ipo_sz,
+ .ymin = ypos - ctx->ipo_size,
+ .ymax = ypos + ctx->ipo_size,
},
true,
3.0f,
@@ -367,7 +367,7 @@ static void draw_keylist_keys(const DrawKeylistUIData *ctx,
draw_keyframe_shape(ak->cfra,
ypos,
- ctx->icon_sz,
+ ctx->icon_size,
(ak->sel & SELECT),
ak->key_type,
KEYFRAME_SHAPE_BOTH,
diff --git a/source/blender/editors/curve/CMakeLists.txt b/source/blender/editors/curve/CMakeLists.txt
index b21ae305dbe..791e28de694 100644
--- a/source/blender/editors/curve/CMakeLists.txt
+++ b/source/blender/editors/curve/CMakeLists.txt
@@ -23,9 +23,9 @@ set(SRC
editcurve.c
editcurve_add.c
editcurve_paint.c
+ editcurve_pen.c
editcurve_query.c
editcurve_select.c
- editcurve_pen.c
editcurve_undo.c
editfont.c
editfont_undo.c
diff --git a/source/blender/editors/curves/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..a2945e8b7e6 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:
@@ -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/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_fileselect.h b/source/blender/editors/include/ED_fileselect.h
index c367072e6e7..e9fcd2bd5fe 100644
--- a/source/blender/editors/include/ED_fileselect.h
+++ b/source/blender/editors/include/ED_fileselect.h
@@ -164,8 +164,16 @@ void ED_fileselect_window_params_get(const struct wmWindow *win,
int win_size[2],
bool *is_maximized);
+/**
+ * Return the File Browser area in which \a file_operator is active.
+ */
struct ScrArea *ED_fileselect_handler_area_find(const struct wmWindow *win,
const struct wmOperator *file_operator);
+/**
+ * Check if there is any area in \a win that acts as a modal File Browser (#SpaceFile.op is set)
+ * and return it.
+ */
+struct ScrArea *ED_fileselect_handler_area_find_any_with_op(const struct wmWindow *win);
/* TODO: Maybe we should move this to BLI?
* On the other hand, it's using defines from space-file area, so not sure... */
diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index 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..414643dd0d6 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -952,6 +952,22 @@ float ED_view3d_select_dist_px(void);
void ED_view3d_viewcontext_init(struct bContext *C,
struct ViewContext *vc,
struct Depsgraph *depsgraph);
+
+/**
+ * Re-initialize `vc` with `obact` as if it's active object (with some differences).
+ *
+ * This is often used when operating on multiple objects in modes (edit, pose mode etc)
+ * where the `vc` is passed in as an argument which then references it's object data.
+ *
+ * \note members #ViewContext.obedit & #ViewContext.em are only initialized if they're already set,
+ * by #ED_view3d_viewcontext_init in most cases.
+ * This is necessary because the active object defines the current object-mode.
+ * When iterating over objects in object-mode it doesn't make sense to perform
+ * an edit-mode action on an object that happens to contain edit-mode data.
+ * In some cases these values are cleared allowing the owner of `vc` to explicitly
+ * disable edit-mode operation (to force object selection in edit-mode for e.g.).
+ * So object-mode specific values should remain cleared when initialized with another object.
+ */
void ED_view3d_viewcontext_init_object(struct ViewContext *vc, struct Object *obact);
/**
* Use this call when executing an operator,
diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h
index d1a6501408c..ea1095b26ff 100644
--- a/source/blender/editors/include/UI_icons.h
+++ b/source/blender/editors/include/UI_icons.h
@@ -164,7 +164,7 @@ DEF_ICON(NLA)
DEF_ICON(PREFERENCES)
DEF_ICON(TIME)
DEF_ICON(NODETREE)
-DEF_ICON_BLANK(181)
+DEF_ICON(GEOMETRY_NODES)
DEF_ICON(CONSOLE)
DEF_ICON_BLANK(183)
DEF_ICON(TRACKER)
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 9f4d6815287..082335ae2cd 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 b7098c26bcd..f316198a160 100644
--- a/source/blender/editors/interface/interface.cc
+++ b/source/blender/editors/interface/interface.cc
@@ -6263,64 +6263,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_style.cc b/source/blender/editors/interface/interface_style.cc
index 0156a943015..291ede05730 100644
--- a/source/blender/editors/interface/interface_style.cc
+++ b/source/blender/editors/interface/interface_style.cc
@@ -376,7 +376,7 @@ void uiStyleInit(void)
{
const uiStyle *style = static_cast<uiStyle *>(U.uistyles.first);
- /* recover from uninitialized dpi */
+ /* Recover from uninitialized DPI. */
if (U.dpi == 0) {
U.dpi = 72;
}
diff --git a/source/blender/editors/interface/view2d_gizmo_navigate.cc b/source/blender/editors/interface/view2d_gizmo_navigate.cc
index 01729e35246..fae28833e4f 100644
--- a/source/blender/editors/interface/view2d_gizmo_navigate.cc
+++ b/source/blender/editors/interface/view2d_gizmo_navigate.cc
@@ -130,6 +130,13 @@ static bool WIDGETGROUP_navigate_poll(const bContext *C, wmGizmoGroupType *UNUSE
}
break;
}
+ case SPACE_IMAGE: {
+ const SpaceImage *sima = static_cast<const SpaceImage *>(area->spacedata.first);
+ if (sima->gizmo_flag & (SI_GIZMO_HIDE | SI_GIZMO_HIDE_NAVIGATE)) {
+ return false;
+ }
+ break;
+ }
}
return true;
}
diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c
index fd454083653..87923d9fdf8 100644
--- a/source/blender/editors/io/io_alembic.c
+++ b/source/blender/editors/io/io_alembic.c
@@ -282,6 +282,7 @@ void WM_OT_alembic_export(wmOperatorType *ot)
ot->poll = WM_operator_winactive;
ot->ui = wm_alembic_export_draw;
ot->check = wm_alembic_export_check;
+ ot->flag |= OPTYPE_PRESET;
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC,
@@ -475,7 +476,7 @@ void WM_OT_alembic_export(wmOperatorType *ot)
/* This dummy prop is used to check whether we need to init the start and
* end frame values to that of the scene's, otherwise they are reset at
* every change, draw update. */
- RNA_def_boolean(ot->srna, "init_scene_frame_range", false, "", "");
+ RNA_def_boolean(ot->srna, "init_scene_frame_range", true, "", "");
}
/* ************************************************************************** */
diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c
index 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, &params, 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/object/object_add.cc b/source/blender/editors/object/object_add.cc
index db8860efdd8..b7deade5146 100644
--- a/source/blender/editors/object/object_add.cc
+++ b/source/blender/editors/object/object_add.cc
@@ -2116,6 +2116,22 @@ static int object_curves_empty_hair_add_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static bool object_curves_empty_hair_add_poll(bContext *C)
+{
+ if (!U.experimental.use_new_curves_type) {
+ return false;
+ }
+ if (!ED_operator_objectmode(C)) {
+ return false;
+ }
+ Object *ob = CTX_data_active_object(C);
+ if (ob == nullptr || ob->type != OB_MESH) {
+ CTX_wm_operator_poll_msg_set(C, "No active mesh object");
+ return false;
+ }
+ return true;
+}
+
void OBJECT_OT_curves_empty_hair_add(wmOperatorType *ot)
{
ot->name = "Add Empty Curves";
@@ -2123,7 +2139,7 @@ void OBJECT_OT_curves_empty_hair_add(wmOperatorType *ot)
ot->idname = "OBJECT_OT_curves_empty_hair_add";
ot->exec = object_curves_empty_hair_add_exec;
- ot->poll = object_curves_add_poll;
+ ot->poll = object_curves_empty_hair_add_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -2762,9 +2778,32 @@ static const EnumPropertyItem convert_target_items[] = {
"Point Cloud",
"Point Cloud from Mesh objects"},
#endif
+ {OB_CURVES, "CURVES", ICON_OUTLINER_OB_CURVES, "Curves", "Curves from evaluated curve data"},
{0, nullptr, 0, nullptr, nullptr},
};
+static const EnumPropertyItem *convert_target_items_fn(bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ EnumPropertyItem *items = nullptr;
+ int items_num = 0;
+ for (const EnumPropertyItem *item = convert_target_items; item->identifier != nullptr; item++) {
+ if (item->value == OB_CURVES) {
+ if (U.experimental.use_new_curves_type) {
+ RNA_enum_item_add(&items, &items_num, item);
+ }
+ }
+ else {
+ RNA_enum_item_add(&items, &items_num, item);
+ }
+ }
+ RNA_enum_item_end(&items, &items_num);
+ *r_free = true;
+ return items;
+}
+
static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
if (ob->runtime.curve_cache == nullptr) {
@@ -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_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/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..5539fda750f 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.
@@ -104,10 +108,11 @@ struct AddOperationExecutor {
bool use_front_face_;
bool interpolate_length_;
bool interpolate_shape_;
+ bool interpolate_point_count_;
bool use_interpolation_;
float new_curve_length_;
int add_amount_;
- int points_per_curve_ = 8;
+ int constant_points_per_curve_;
/** Various matrices to convert between coordinate spaces. */
float4x4 curves_to_world_mat_;
@@ -128,6 +133,15 @@ struct AddOperationExecutor {
Vector<int> looptri_indices;
};
+ struct NeighborInfo {
+ /* Curve index of the neighbor. */
+ int index;
+ /* The weights of all neighbors of a new curve add up to 1. */
+ float weight;
+ };
+ static constexpr int max_neighbors = 5;
+ using NeighborsVector = Vector<NeighborInfo, max_neighbors>;
+
void execute(AddOperation &self, bContext *C, const StrokeExtension &stroke_extension)
{
self_ = &self;
@@ -171,9 +185,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 +200,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 +213,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 +230,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 +271,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 +324,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 +352,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 +395,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 +403,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 +417,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 +502,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 +583,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 +604,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 +768,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 +796,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 +845,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 +856,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;
}
}
}
@@ -786,8 +870,16 @@ void AddOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke
executor.execute(*this, C, stroke_extension);
}
-std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation()
+std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(bContext &C, ReportList *reports)
{
+ Object &ob_active = *CTX_data_active_object(&C);
+ BLI_assert(ob_active.type == OB_CURVES);
+ Curves &curves_id = *static_cast<Curves *>(ob_active.data);
+ if (curves_id.surface == nullptr || curves_id.surface->type != OB_MESH) {
+ BKE_report(reports, RPT_WARNING, "Can not use Add brush when there is no surface mesh");
+ return {};
+ }
+
return std::make_unique<AddOperation>();
}
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc
index 945bb09c0c6..89470772e1c 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc
@@ -116,8 +116,11 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu
const float distance_sq_re = math::distance_squared(brush_pos_re, closest_re);
+ float3 brush_position_cu;
+ closest_to_line_segment_v3(brush_position_cu, closest_cu, ray_start_cu, ray_end_cu);
+
BrushPositionCandidate candidate;
- candidate.position_cu = closest_cu;
+ candidate.position_cu = brush_position_cu;
candidate.depth_sq_cu = depth_sq_cu;
candidate.distance_sq_re = distance_sq_re;
@@ -229,4 +232,32 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C,
return brush_3d;
}
+Vector<float4x4> get_symmetry_brush_transforms(const eCurvesSymmetryType symmetry)
+{
+ Vector<float4x4> matrices;
+
+ auto symmetry_to_factors = [&](const eCurvesSymmetryType type) -> Span<float> {
+ if (symmetry & type) {
+ static std::array<float, 2> values = {1.0f, -1.0f};
+ return values;
+ }
+ static std::array<float, 1> values = {1.0f};
+ return values;
+ };
+
+ for (const float x : symmetry_to_factors(CURVES_SYMMETRY_X)) {
+ for (const float y : symmetry_to_factors(CURVES_SYMMETRY_Y)) {
+ for (const float z : symmetry_to_factors(CURVES_SYMMETRY_Z)) {
+ float4x4 matrix = float4x4::identity();
+ matrix.values[0][0] = x;
+ matrix.values[1][1] = y;
+ matrix.values[2][2] = z;
+ matrices.append(matrix);
+ }
+ }
+ }
+
+ return matrices;
+}
+
} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc
index 232d632aa3f..41199be8886 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.
@@ -173,10 +175,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 +188,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 +222,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 +247,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 +263,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 +282,26 @@ struct CombOperationExecutor {
const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo;
const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo;
- const float3 brush_diff_cu = brush_end_cu - brush_start_cu;
-
const float brush_radius_cu = self_->brush_3d_.radius_cu;
+
+ const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
+ eCurvesSymmetryType(curves_id_->symmetry));
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ this->comb_spherical(r_changed_curves,
+ brush_transform * brush_start_cu,
+ brush_transform * brush_end_cu,
+ brush_radius_cu);
+ }
+ }
+
+ void comb_spherical(EnumerableThreadSpecific<Vector<int>> &r_changed_curves,
+ const float3 &brush_start_cu,
+ const float3 &brush_end_cu,
+ const float brush_radius_cu)
+ {
+ MutableSpan<float3> positions_cu = curves_->positions_for_write();
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+ const float3 brush_diff_cu = brush_end_cu - brush_start_cu;
threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
Vector<int> &local_changed_curves = r_changed_curves.local();
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc
index 2841a19d677..a4962fc6b5a 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.
@@ -117,56 +119,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(curves_to_delete, brush_transform);
+ }
+ }
+
+ void delete_projected(MutableSpan<bool> curves_to_delete, const float4x4 &brush_transform)
{
+ const float4x4 brush_transform_inv = brush_transform.inverted();
+
float4x4 projection;
ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values);
Span<float3> positions_cu = curves_->positions();
- /* Find indices of curves that have to be removed. */
- Vector<int64_t> indices;
- const IndexMask curves_to_remove = index_mask_ops::find_indices_based_on_predicate(
- curves_->curves_range(), 512, indices, [&](const int curve_i) {
- const IndexRange point_range = curves_->points_for_curve(curve_i);
- for (const int segment_i : IndexRange(point_range.size() - 1)) {
- const float3 pos1_cu = positions_cu[point_range[segment_i]];
- const float3 pos2_cu = positions_cu[point_range[segment_i + 1]];
-
- float2 pos1_re, pos2_re;
- ED_view3d_project_float_v2_m4(region_, pos1_cu, pos1_re, projection.values);
- ED_view3d_project_float_v2_m4(region_, pos2_cu, pos2_re, projection.values);
-
- const float dist = dist_seg_seg_v2(
- pos1_re, pos2_re, brush_pos_prev_re_, brush_pos_re_);
- if (dist <= brush_radius_re_) {
- return true;
- }
+ threading::parallel_for(curves_->curves_range(), 512, [&](IndexRange curve_range) {
+ for (const int curve_i : curve_range) {
+ const IndexRange point_range = curves_->points_for_curve(curve_i);
+ for (const int segment_i : IndexRange(point_range.size() - 1)) {
+ const float3 pos1_cu = brush_transform_inv * positions_cu[point_range[segment_i]];
+ const float3 pos2_cu = brush_transform_inv * positions_cu[point_range[segment_i + 1]];
+
+ float2 pos1_re, pos2_re;
+ ED_view3d_project_float_v2_m4(region_, pos1_cu, pos1_re, projection.values);
+ ED_view3d_project_float_v2_m4(region_, pos2_cu, pos2_re, projection.values);
+
+ const float dist = dist_seg_seg_v2(pos1_re, pos2_re, brush_pos_prev_re_, brush_pos_re_);
+ if (dist <= brush_radius_re_) {
+ curves_to_delete[curve_i] = true;
+ break;
}
- return false;
- });
-
- curves_->remove_curves(curves_to_remove);
+ }
+ }
+ });
}
- void delete_spherical()
+ void delete_spherical_with_symmetry(MutableSpan<bool> curves_to_delete)
{
- Span<float3> positions_cu = curves_->positions();
-
float4x4 projection;
ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values);
@@ -184,35 +201,48 @@ struct DeleteOperationExecutor {
const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo;
const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo;
+ const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
+ eCurvesSymmetryType(curves_id_->symmetry));
+
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ this->delete_spherical(
+ curves_to_delete, brush_transform * brush_start_cu, brush_transform * brush_end_cu);
+ }
+ }
+
+ void delete_spherical(MutableSpan<bool> curves_to_delete,
+ const float3 &brush_start_cu,
+ const float3 &brush_end_cu)
+ {
+ Span<float3> positions_cu = curves_->positions();
+
const float brush_radius_cu = self_->brush_3d_.radius_cu;
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
- Vector<int64_t> indices;
- const IndexMask curves_to_remove = index_mask_ops::find_indices_based_on_predicate(
- curves_->curves_range(), 512, indices, [&](const int curve_i) {
- const IndexRange points = curves_->points_for_curve(curve_i);
- for (const int segment_i : IndexRange(points.size() - 1)) {
- const float3 pos1_cu = positions_cu[points[segment_i]];
- const float3 pos2_cu = positions_cu[points[segment_i] + 1];
-
- float3 closest_segment_cu, closest_brush_cu;
- isect_seg_seg_v3(pos1_cu,
- pos2_cu,
- brush_start_cu,
- brush_end_cu,
- closest_segment_cu,
- closest_brush_cu);
- const float distance_to_brush_sq_cu = math::distance_squared(closest_segment_cu,
- closest_brush_cu);
- if (distance_to_brush_sq_cu > brush_radius_sq_cu) {
- continue;
- }
- return true;
+ threading::parallel_for(curves_->curves_range(), 512, [&](IndexRange curve_range) {
+ for (const int curve_i : curve_range) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ for (const int segment_i : IndexRange(points.size() - 1)) {
+ const float3 pos1_cu = positions_cu[points[segment_i]];
+ const float3 pos2_cu = positions_cu[points[segment_i] + 1];
+
+ float3 closest_segment_cu, closest_brush_cu;
+ isect_seg_seg_v3(pos1_cu,
+ pos2_cu,
+ brush_start_cu,
+ brush_end_cu,
+ closest_segment_cu,
+ closest_brush_cu);
+ const float distance_to_brush_sq_cu = math::distance_squared(closest_segment_cu,
+ closest_brush_cu);
+ if (distance_to_brush_sq_cu > brush_radius_sq_cu) {
+ continue;
}
- return false;
- });
-
- curves_->remove_curves(curves_to_remove);
+ curves_to_delete[curve_i] = true;
+ break;
+ }
+ }
+ });
}
void initialize_spherical_brush_reference_point()
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc
index 6228a643a76..e4963fd4ca4 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"
/**
@@ -356,6 +358,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 +370,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 +384,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 +468,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 +479,37 @@ struct CurvesEffectOperationExecutor {
const IndexRange points = curves_->points_for_curve(curve_i);
const int tot_segments = points.size() - 1;
float max_move_distance_cu = 0.0f;
- for (const int segment_i : IndexRange(tot_segments)) {
- const float3 &p1_cu = positions_cu[points[segment_i]];
- const float3 &p2_cu = positions_cu[points[segment_i] + 1];
-
- float3 closest_on_segment_cu;
- float3 closest_on_brush_cu;
- isect_seg_seg_v3(p1_cu,
- p2_cu,
- brush_pos_start_cu,
- brush_pos_end_cu,
- closest_on_segment_cu,
- closest_on_brush_cu);
-
- const float dist_to_brush_sq_cu = math::distance_squared(closest_on_segment_cu,
- closest_on_brush_cu);
- if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
- continue;
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ const float3 brush_pos_start_transformed_cu = brush_transform * brush_pos_start_cu;
+ const float3 brush_pos_end_transformed_cu = brush_transform * brush_pos_end_cu;
+
+ for (const int segment_i : IndexRange(tot_segments)) {
+ const float3 &p1_cu = positions_cu[points[segment_i]];
+ const float3 &p2_cu = positions_cu[points[segment_i] + 1];
+
+ float3 closest_on_segment_cu;
+ float3 closest_on_brush_cu;
+ isect_seg_seg_v3(p1_cu,
+ p2_cu,
+ brush_pos_start_transformed_cu,
+ brush_pos_end_transformed_cu,
+ closest_on_segment_cu,
+ closest_on_brush_cu);
+
+ const float dist_to_brush_sq_cu = math::distance_squared(closest_on_segment_cu,
+ closest_on_brush_cu);
+ if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
+ continue;
+ }
+
+ const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu);
+ const float radius_falloff = BKE_brush_curve_strength(
+ brush_, dist_to_brush_cu, brush_radius_cu);
+ const float weight = brush_strength_ * radius_falloff;
+
+ const float move_distance_cu = weight * brush_pos_diff_length_cu;
+ max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu);
}
-
- const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu);
- const float radius_falloff = BKE_brush_curve_strength(
- brush_, dist_to_brush_cu, brush_radius_cu);
- const float weight = brush_strength_ * radius_falloff;
-
- const float move_distance_cu = weight * brush_pos_diff_length_cu;
- max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu);
}
if (max_move_distance_cu > 0.0f) {
local_influences.curve_indices.append(curve_i);
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
index 03413221907..9d000649d62 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
@@ -33,7 +33,7 @@ class CurvesSculptStrokeOperation {
virtual void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) = 0;
};
-std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation();
+std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(bContext &C, ReportList *reports);
std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation();
std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation();
std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation();
@@ -53,4 +53,6 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C,
const float2 &brush_pos_re,
float brush_radius_re);
+Vector<float4x4> get_symmetry_brush_transforms(eCurvesSymmetryType symmetry);
+
} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
index 992ac77803a..d8713c8eb1d 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
@@ -74,12 +74,12 @@ using blender::bke::CurvesGeometry;
/** \name * SCULPT_CURVES_OT_brush_stroke
* \{ */
-static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext *C,
- wmOperator *op)
+static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext &C,
+ wmOperator &op)
{
- const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op->ptr, "mode"));
+ const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op.ptr, "mode"));
- Scene &scene = *CTX_data_scene(C);
+ Scene &scene = *CTX_data_scene(&C);
CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt;
Brush &brush = *BKE_paint_brush(&curves_sculpt.paint);
switch (brush.curves_sculpt_tool) {
@@ -90,9 +90,9 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bConte
case CURVES_SCULPT_TOOL_SNAKE_HOOK:
return new_snake_hook_operation();
case CURVES_SCULPT_TOOL_ADD:
- return new_add_operation();
+ return new_add_operation(C, op.reports);
case CURVES_SCULPT_TOOL_GROW_SHRINK:
- return new_grow_shrink_operation(mode, C);
+ return new_grow_shrink_operation(mode, &C);
}
BLI_assert_unreachable();
return {};
@@ -131,7 +131,7 @@ static void stroke_update_step(bContext *C,
if (!op_data->operation) {
stroke_extension.is_first = true;
- op_data->operation = start_brush_operation(C, op);
+ op_data->operation = start_brush_operation(*C, *op);
}
else {
stroke_extension.is_first = false;
@@ -149,6 +149,12 @@ static void stroke_done(const bContext *C, PaintStroke *stroke)
static int sculpt_curves_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
+ Paint *paint = BKE_paint_get_active_from_context(C);
+ Brush *brush = BKE_paint_brush(paint);
+ if (brush == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+
SculptCurvesBrushStrokeData *op_data = MEM_new<SculptCurvesBrushStrokeData>(__func__);
op_data->stroke = paint_stroke_new(C,
op,
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
index 6d930d35f04..973751e9045 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.
@@ -136,10 +138,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 +149,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 +175,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 +193,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 +218,23 @@ struct SnakeHookOperatorExecutor {
brush_end_wo);
const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo;
const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo;
- const float3 brush_diff_cu = brush_end_cu - brush_start_cu;
const float brush_radius_cu = self_->brush_3d_.radius_cu;
+
+ const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
+ eCurvesSymmetryType(curves_id_->symmetry));
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
+ this->spherical_snake_hook(
+ brush_transform * brush_start_cu, brush_transform * brush_end_cu, brush_radius_cu);
+ }
+ }
+
+ void spherical_snake_hook(const float3 &brush_start_cu,
+ const float3 &brush_end_cu,
+ const float brush_radius_cu)
+ {
+ MutableSpan<float3> positions_cu = curves_->positions_for_write();
+ const float3 brush_diff_cu = brush_end_cu - brush_start_cu;
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index e442cd53639..2fd8bc1ba3f 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"
@@ -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_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
index 9bddc2ad855..f71a814aff4 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -125,7 +125,7 @@ static void color_filter_task_cb(void *__restrict userdata,
case COLOR_FILTER_HUE:
rgb_to_hsv_v(orig_color, hsv_color);
hue = hsv_color[0];
- hsv_color[0] = fmod((hsv_color[0] + fabs(fade)) - hue,1);
+ hsv_color[0] = fmod((hsv_color[0] + fabs(fade)) - hue, 1);
hsv_to_rgb_v(hsv_color, final_color);
break;
case COLOR_FILTER_SATURATION:
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..8e1df133189 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 rules w.r.t distance tolerances for track sliding and selection operators. */
+struct MovieTrackingTrack *tracking_find_slidable_track_in_proximity(struct bContext *C,
+ const struct wmEvent *event);
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, &region->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, &region_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, &region_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, &region_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, &region_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, &region_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..239e9925997 100644
--- a/source/blender/editors/space_clip/tracking_ops.c
+++ b/source/blender/editors/space_clip/tracking_ops.c
@@ -336,15 +336,18 @@ 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;
@@ -373,7 +376,7 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc,
const wmEvent *event,
int area,
int corner,
- int action,
+ eSlideAction action,
int width,
int height)
{
@@ -392,26 +395,32 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc,
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) {
- 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);
+ switch (action) {
+ case SLIDE_ACTION_NONE:
+ BLI_assert_msg(0, "Expected valid action");
+ break;
+
+ case SLIDE_ACTION_SIZE:
+ data->corners = marker->pattern_corners;
+ break;
+ case 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);
+ }
+ break;
+ case SLIDE_ACTION_POS:
+ data->corners = marker->pattern_corners;
+ data->pos = marker->pattern_corners[corner];
+ copy_v2_v2(data->spos, data->pos);
+ break;
+ case SLIDE_ACTION_TILT_SIZE:
+ data->corners = marker->pattern_corners;
+ slide_marker_tilt_slider(marker, data->spos);
+ break;
}
}
else if (area == TRACK_AREA_SEARCH) {
@@ -534,117 +543,100 @@ 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 wmEvent *event, 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;
}
+ float co[2];
ED_clip_mouse_pos(sc, region, event->mval, co);
- 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;
-
- /* 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.
- */
- min_distance_squared = distance_squared;
+ LISTBASE_FOREACH (MovieTrackingTrack *, track, tracksbase) {
+ if (!TRACK_VIEW_SELECTED(sc, track) || (track->flag & TRACK_LOCKED)) {
+ continue;
+ }
- /* 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;
- }
+ const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
+ if (marker->flag & MARKER_DISABLED) {
+ continue;
+ }
- 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;
- }
- }
+ /* 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 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, &current_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, &current_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 +651,16 @@ MovieTrackingTrack *tracking_marker_check_slide(
}
return min_track;
}
+
return NULL;
}
+struct MovieTrackingTrack *tracking_find_slidable_track_in_proximity(struct bContext *C,
+ const struct wmEvent *event)
+{
+ return tracking_marker_check_slide(C, event, NULL, NULL, NULL);
+}
+
static void *slide_marker_customdata(bContext *C, const wmEvent *event)
{
SpaceClip *sc = CTX_wm_space_clip(C);
@@ -672,7 +671,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);
@@ -849,7 +849,7 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
}
- BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
+ BKE_tracking_marker_clamp_search_size(data->marker);
}
else if (data->action == SLIDE_ACTION_OFFSET) {
const float d[2] = {dx, dy};
@@ -870,10 +870,8 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event)
copy_v2_v2(data->pos, spos);
}
- /* Currently only patterns are allowed to have such
- * combination of event and data.
- */
- BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
+ /* Currently only patterns are allowed to have such combination of event and data. */
+ BKE_tracking_marker_clamp_search_size(data->marker);
}
else if (data->action == SLIDE_ACTION_TILT_SIZE) {
const float mouse_delta[2] = {dx, dy};
@@ -917,7 +915,7 @@ 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) {
@@ -928,7 +926,7 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event)
data->min[1] = data->old_search_min[1] + dy;
data->max[1] = data->old_search_max[1] - dy;
- BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_DIM);
+ BKE_tracking_marker_clamp_search_size(data->marker);
}
else if (data->area == TRACK_AREA_SEARCH) {
const float d[2] = {dx, dy};
@@ -936,7 +934,7 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event)
add_v2_v2v2(data->max, data->old_search_max, d);
}
- BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_POS);
+ BKE_tracking_marker_clamp_search_position(data->marker);
}
data->marker->flag &= ~MARKER_TRACKED;
@@ -1012,9 +1010,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 5f940b817a9..5b2a1b945e7 100644
--- a/source/blender/editors/space_clip/tracking_select.c
+++ b/source/blender/editors/space_clip/tracking_select.c
@@ -405,10 +405,13 @@ static int select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
float co[2];
const bool extend = RNA_boolean_get(op->ptr, "extend");
+ /* 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_marker_check_slide(C, event, NULL, NULL, NULL);
-
- if (track) {
+ MovieTrackingTrack *track = tracking_find_slidable_track_in_proximity(C, event);
+ if (track != NULL) {
MovieClip *clip = ED_space_clip_get_clip(sc);
clip->tracking.act_track = track;
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_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index aa77aab2283..0cfb924e618 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -2316,6 +2316,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 +2347,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,7 +2404,7 @@ 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) {
@@ -3040,9 +3048,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 +3117,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 +3150,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_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 4157176cd68..f1653e3dfd6 100644
--- a/source/blender/editors/space_node/node_intern.hh
+++ b/source/blender/editors/space_node/node_intern.hh
@@ -74,13 +74,17 @@ struct SpaceNode_Runtime {
/** Mouse position for drawing socket-less links and adding nodes. */
float2 cursor;
- /* Indicates that the compositing tree in the space needs to be re-evaluated using the
+ /**
+ * Indicates that the compositing tree in the space needs to be re-evaluated using the
* auto-compositing pipeline.
- * Takes priority over the regular compsiting. */
+ * Takes priority over the regular compositing.
+ */
bool recalc_auto_compositing;
- /* Indicates that the compositing int the space tree needs to be re-evaluated using
- * regular compositing pipeline. */
+ /**
+ * Indicates that the compositing int the space tree needs to be re-evaluated using
+ * regular compositing pipeline.
+ */
bool recalc_regular_compositing;
/** Temporary data for modal linking operator. */
@@ -100,7 +104,7 @@ enum NodeResizeDirection {
};
ENUM_OPERATORS(NodeResizeDirection, NODE_RESIZE_LEFT);
-/* Nodes draw without dpi - the view zoom is flexible. */
+/* Nodes draw without DPI - the view zoom is flexible. */
#define HIDDEN_RAD (0.75f * U.widget_unit)
#define BASIS_RAD (0.2f * U.widget_unit)
#define NODE_DYS (U.widget_unit / 2)
diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc
index 296cd1ff133..348fb926d17 100644
--- a/source/blender/editors/space_node/space_node.cc
+++ b/source/blender/editors/space_node/space_node.cc
@@ -327,8 +327,9 @@ static bool any_node_uses_id(const bNodeTree *ntree, const ID *id)
/**
* Tag the space to recalculate the compositing tree using auto-compositing pipeline.
*
- * Will check the space to be using a compsiting tree, and check whether auto-compositing
- * is enabled. If the checks do not pass then the function has no affect. */
+ * Will check the space to be using a compositing tree, and check whether auto-compositing
+ * is enabled. If the checks do not pass then the function has no affect.
+ */
static void node_area_tag_recalc_auto_compositing(SpaceNode *snode, ScrArea *area)
{
if (!ED_node_is_compositor(snode)) {
@@ -347,7 +348,8 @@ static void node_area_tag_recalc_auto_compositing(SpaceNode *snode, ScrArea *are
* For all node trees this will do `snode_set_context()` which takes care of setting an active
* tree. This will be done in the area refresh callback.
*
- * For compositor tree this will additionally start of the compositor job. */
+ * For compositor tree this will additionally start of the compositor job.
+ */
static void node_area_tag_tree_recalc(SpaceNode *snode, ScrArea *area)
{
if (ED_node_is_compositor(snode)) {
diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt
index fae0e4be2a8..59f6bd85d59 100644
--- a/source/blender/editors/space_outliner/CMakeLists.txt
+++ b/source/blender/editors/space_outliner/CMakeLists.txt
@@ -38,8 +38,8 @@ set(SRC
tree/tree_display_data.cc
tree/tree_display_libraries.cc
tree/tree_display_orphaned.cc
- tree/tree_display_override_library_properties.cc
tree/tree_display_override_library_hierarchies.cc
+ tree/tree_display_override_library_properties.cc
tree/tree_display_scenes.cc
tree/tree_display_sequencer.cc
tree/tree_display_view_layer.cc
diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc
index 36e21cf51a5..57b0e2022ff 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"
@@ -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..f4e28af3fca 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;
@@ -568,187 +576,6 @@ void OUTLINER_OT_id_delete(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name ID Remap Operator
- * \{ */
-
-static int outliner_id_remap_exec(bContext *C, wmOperator *op)
-{
- Main *bmain = CTX_data_main(C);
- SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
-
- const short id_type = (short)RNA_enum_get(op->ptr, "id_type");
- ID *old_id = reinterpret_cast<ID *>(
- BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "old_id")));
- ID *new_id = reinterpret_cast<ID *>(
- BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "new_id")));
-
- /* check for invalid states */
- if (space_outliner == nullptr) {
- return OPERATOR_CANCELLED;
- }
-
- if (!(old_id && new_id && (old_id != new_id) && (GS(old_id->name) == GS(new_id->name)))) {
- BKE_reportf(op->reports,
- RPT_ERROR_INVALID_INPUT,
- "Invalid old/new ID pair ('%s' / '%s')",
- old_id ? old_id->name : "Invalid ID",
- new_id ? new_id->name : "Invalid ID");
- return OPERATOR_CANCELLED;
- }
-
- if (ID_IS_LINKED(old_id)) {
- BKE_reportf(op->reports,
- RPT_WARNING,
- "Old ID '%s' is linked from a library, indirect usages of this data-block will "
- "not be remapped",
- old_id->name);
- }
-
- BKE_libblock_remap(
- bmain, old_id, new_id, ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_NEVER_NULL_USAGE);
-
- BKE_main_lib_objects_recalc_all(bmain);
-
- /* recreate dependency graph to include new objects */
- DEG_relations_tag_update(bmain);
-
- /* Free gpu materials, some materials depend on existing objects,
- * such as lights so freeing correctly refreshes. */
- GPU_materials_free(bmain);
-
- WM_event_add_notifier(C, NC_WINDOW, nullptr);
-
- return OPERATOR_FINISHED;
-}
-
-static bool outliner_id_remap_find_tree_element(bContext *C,
- wmOperator *op,
- ListBase *tree,
- const float y)
-{
- LISTBASE_FOREACH (TreeElement *, te, tree) {
- if (y > te->ys && y < te->ys + UI_UNIT_Y) {
- TreeStoreElem *tselem = TREESTORE(te);
-
- if ((tselem->type == TSE_SOME_ID) && tselem->id) {
- RNA_enum_set(op->ptr, "id_type", GS(tselem->id->name));
- RNA_enum_set_identifier(C, op->ptr, "new_id", tselem->id->name + 2);
- RNA_enum_set_identifier(C, op->ptr, "old_id", tselem->id->name + 2);
- return true;
- }
- }
- if (outliner_id_remap_find_tree_element(C, op, &te->subtree, y)) {
- return true;
- }
- }
- return false;
-}
-
-static int outliner_id_remap_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
- ARegion *region = CTX_wm_region(C);
- float fmval[2];
-
- if (!RNA_property_is_set(op->ptr, RNA_struct_find_property(op->ptr, "id_type"))) {
- UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
-
- outliner_id_remap_find_tree_element(C, op, &space_outliner->tree, fmval[1]);
- }
-
- return WM_operator_props_dialog_popup(C, op, 400);
-}
-
-static const EnumPropertyItem *outliner_id_itemf(bContext *C,
- PointerRNA *ptr,
- PropertyRNA *UNUSED(prop),
- bool *r_free)
-{
- if (C == nullptr) {
- return DummyRNA_NULL_items;
- }
-
- EnumPropertyItem item_tmp = {0}, *item = nullptr;
- int totitem = 0;
- int i = 0;
-
- short id_type = (short)RNA_enum_get(ptr, "id_type");
- ID *id = reinterpret_cast<ID *>(which_libbase(CTX_data_main(C), id_type)->first);
-
- for (; id; id = reinterpret_cast<ID *>(id->next)) {
- item_tmp.identifier = item_tmp.name = id->name + 2;
- item_tmp.value = i++;
- RNA_enum_item_add(&item, &totitem, &item_tmp);
- }
-
- RNA_enum_item_end(&item, &totitem);
- *r_free = true;
-
- return item;
-}
-
-void OUTLINER_OT_id_remap(wmOperatorType *ot)
-{
- PropertyRNA *prop;
-
- /* identifiers */
- ot->name = "Outliner ID Data Remap";
- ot->idname = "OUTLINER_OT_id_remap";
-
- /* callbacks */
- ot->invoke = outliner_id_remap_invoke;
- ot->exec = outliner_id_remap_exec;
- ot->poll = ED_operator_outliner_active;
-
- /* Flags. */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- prop = RNA_def_enum(ot->srna, "id_type", rna_enum_id_type_items, ID_OB, "ID Type", "");
- RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID);
- /* Changing ID type wont make sense, would return early with "Invalid old/new ID pair" anyways.
- */
- RNA_def_property_flag(prop, PROP_HIDDEN);
-
- prop = RNA_def_enum(ot->srna, "old_id", DummyRNA_NULL_items, 0, "Old ID", "Old ID to replace");
- RNA_def_property_enum_funcs_runtime(prop, nullptr, nullptr, outliner_id_itemf);
- RNA_def_property_flag(prop, (PropertyFlag)(PROP_ENUM_NO_TRANSLATE | PROP_HIDDEN));
-
- ot->prop = RNA_def_enum(ot->srna,
- "new_id",
- DummyRNA_NULL_items,
- 0,
- "New ID",
- "New ID to remap all selected IDs' users to");
- RNA_def_property_enum_funcs_runtime(ot->prop, nullptr, nullptr, outliner_id_itemf);
- RNA_def_property_flag(ot->prop, PROP_ENUM_NO_TRANSLATE);
-}
-
-void id_remap_fn(bContext *C,
- ReportList *UNUSED(reports),
- Scene *UNUSED(scene),
- TreeElement *UNUSED(te),
- TreeStoreElem *UNUSED(tsep),
- TreeStoreElem *tselem,
- void *UNUSED(user_data))
-{
- wmOperatorType *ot = WM_operatortype_find("OUTLINER_OT_id_remap", false);
- PointerRNA op_props;
-
- BLI_assert(tselem->id != nullptr);
-
- WM_operator_properties_create_ptr(&op_props, ot);
-
- RNA_enum_set(&op_props, "id_type", GS(tselem->id->name));
- RNA_enum_set_identifier(C, &op_props, "old_id", tselem->id->name + 2);
-
- WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_props, nullptr);
-
- WM_operator_properties_free(&op_props);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name ID Copy Operator
* \{ */
diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh
index f3bcb7b0f1e..2fdf327cbee 100644
--- a/source/blender/editors/space_outliner/outliner_intern.hh
+++ b/source/blender/editors/space_outliner/outliner_intern.hh
@@ -442,13 +442,6 @@ void id_delete_fn(struct bContext *C,
struct TreeStoreElem *tsep,
struct TreeStoreElem *tselem,
void *user_data);
-void id_remap_fn(struct bContext *C,
- struct ReportList *reports,
- struct Scene *scene,
- struct TreeElement *te,
- struct TreeStoreElem *tsep,
- struct TreeStoreElem *tselem,
- void *user_data);
/**
* To retrieve coordinates with redrawing the entire tree.
@@ -523,7 +516,6 @@ void OUTLINER_OT_scene_operation(struct wmOperatorType *ot);
void OUTLINER_OT_object_operation(struct wmOperatorType *ot);
void OUTLINER_OT_lib_operation(struct wmOperatorType *ot);
void OUTLINER_OT_id_operation(struct wmOperatorType *ot);
-void OUTLINER_OT_id_remap(struct wmOperatorType *ot);
void OUTLINER_OT_id_copy(struct wmOperatorType *ot);
void OUTLINER_OT_id_paste(struct wmOperatorType *ot);
void OUTLINER_OT_data_operation(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_outliner/outliner_ops.cc b/source/blender/editors/space_outliner/outliner_ops.cc
index 8baac45666e..e053a94c572 100644
--- a/source/blender/editors/space_outliner/outliner_ops.cc
+++ b/source/blender/editors/space_outliner/outliner_ops.cc
@@ -31,7 +31,6 @@ void outliner_operatortypes(void)
WM_operatortype_append(OUTLINER_OT_lib_relocate);
WM_operatortype_append(OUTLINER_OT_id_operation);
WM_operatortype_append(OUTLINER_OT_id_delete);
- WM_operatortype_append(OUTLINER_OT_id_remap);
WM_operatortype_append(OUTLINER_OT_id_copy);
WM_operatortype_append(OUTLINER_OT_id_paste);
WM_operatortype_append(OUTLINER_OT_data_operation);
diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc
index 5da64177e51..f10edc29e37 100644
--- a/source/blender/editors/space_outliner/outliner_tools.cc
+++ b/source/blender/editors/space_outliner/outliner_tools.cc
@@ -1692,7 +1692,6 @@ enum {
OL_OP_SELECT = 1,
OL_OP_DESELECT,
OL_OP_SELECT_HIERARCHY,
- OL_OP_REMAP,
OL_OP_RENAME,
};
@@ -1700,11 +1699,6 @@ static const EnumPropertyItem prop_object_op_types[] = {
{OL_OP_SELECT, "SELECT", ICON_RESTRICT_SELECT_OFF, "Select", ""},
{OL_OP_DESELECT, "DESELECT", 0, "Deselect", ""},
{OL_OP_SELECT_HIERARCHY, "SELECT_HIERARCHY", 0, "Select Hierarchy", ""},
- {OL_OP_REMAP,
- "REMAP",
- 0,
- "Remap Users",
- "Make all users of selected data-blocks to use instead a new chosen one"},
{OL_OP_RENAME, "RENAME", 0, "Rename", ""},
{0, nullptr, 0, nullptr, nullptr},
};
@@ -1759,12 +1753,6 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op)
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);
@@ -1973,7 +1961,6 @@ enum eOutlinerIdOpTypes {
OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE,
OUTLINER_IDOP_SINGLE,
OUTLINER_IDOP_DELETE,
- OUTLINER_IDOP_REMAP,
OUTLINER_IDOP_COPY,
OUTLINER_IDOP_PASTE,
@@ -1991,11 +1978,6 @@ static const EnumPropertyItem prop_id_op_types[] = {
{OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""},
{OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""},
{OUTLINER_IDOP_DELETE, "DELETE", ICON_X, "Delete", ""},
- {OUTLINER_IDOP_REMAP,
- "REMAP",
- 0,
- "Remap Users",
- "Make all users of selected data-blocks to use instead current (clicked) one"},
{0, "", 0, nullptr, nullptr},
{OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
"OVERRIDE_LIBRARY_CREATE",
@@ -2414,15 +2396,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
break;
}
- case OUTLINER_IDOP_REMAP: {
- if (idlevel > 0) {
- 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 OUTLINER_IDOP_COPY: {
wm->op_undo_depth++;
WM_operator_name_call(C, "OUTLINER_OT_id_copy", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
@@ -2527,14 +2500,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 +2537,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_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
index 4afa70d9ef6..66eced27b32 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -64,7 +64,7 @@ std::unique_ptr<ColumnValues> ExtraColumns::get_column_values(
void GeometryDataSource::foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
- if (component_->attribute_domain_size(domain_) == 0) {
+ if (component_->attribute_domain_num(domain_) == 0) {
return;
}
@@ -110,8 +110,8 @@ void GeometryDataSource::foreach_default_column_ids(
std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
const SpreadsheetColumnID &column_id) const
{
- const int domain_size = component_->attribute_domain_size(domain_);
- if (domain_size == 0) {
+ const int domain_num = component_->attribute_domain_num(domain_);
+ if (domain_num == 0) {
return {};
}
@@ -129,7 +129,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
Span<InstanceReference> references = instances.references();
return std::make_unique<ColumnValues>(
column_id.name,
- VArray<InstanceReference>::ForFunc(domain_size,
+ VArray<InstanceReference>::ForFunc(domain_num,
[reference_handles, references](int64_t index) {
return references[reference_handles[index]];
}));
@@ -137,13 +137,13 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
Span<float4x4> transforms = instances.instance_transforms();
if (STREQ(column_id.name, "Rotation")) {
return std::make_unique<ColumnValues>(
- column_id.name, VArray<float3>::ForFunc(domain_size, [transforms](int64_t index) {
+ column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) {
return transforms[index].to_euler();
}));
}
if (STREQ(column_id.name, "Scale")) {
return std::make_unique<ColumnValues>(
- column_id.name, VArray<float3>::ForFunc(domain_size, [transforms](int64_t index) {
+ column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) {
return transforms[index].scale();
}));
}
@@ -210,7 +210,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
int GeometryDataSource::tot_rows() const
{
- return component_->attribute_domain_size(domain_);
+ return component_->attribute_domain_num(domain_);
}
/**
@@ -524,17 +524,17 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet,
std::make_unique<GeometryComponentCacheKey>(component));
const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain;
- const int domain_size = component.attribute_domain_size(domain);
+ const int domain_num = component.attribute_domain_num(domain);
for (const auto item : fields_to_show.items()) {
StringRef name = item.key;
const GField &field = item.value;
/* Use the cached evaluated array if it exists, otherwise evaluate the field now. */
GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() {
- GArray<> evaluated_array(field.cpp_type(), domain_size);
+ GArray<> evaluated_array(field.cpp_type(), domain_num);
bke::GeometryComponentFieldContext field_context{component, domain};
- fn::FieldEvaluator field_evaluator{field_context, domain_size};
+ fn::FieldEvaluator field_evaluator{field_context, domain_num};
field_evaluator.add_with_destination(field, evaluated_array);
field_evaluator.evaluate();
return evaluated_array;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc
index c4b5228758c..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/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index ffd33006cc2..7a022441876 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -126,6 +126,7 @@ void ED_view3d_viewcontext_init(bContext *C, ViewContext *vc, Depsgraph *depsgra
void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact)
{
vc->obact = obact;
+ /* See public doc-string for rationale on checking the existing values first. */
if (vc->obedit) {
BLI_assert(BKE_object_is_in_editmode(obact));
vc->obedit = obact;
diff --git a/source/blender/editors/transform/transform_convert.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 45f17512c09..90ca2805008 100644
--- a/source/blender/editors/transform/transform_convert_tracking.c
+++ b/source/blender/editors/transform/transform_convert_tracking.c
@@ -712,23 +712,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/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index 1287804d9ee..13dac431b57 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -1019,8 +1019,13 @@ bool uv_find_nearest_vert_multi(Scene *scene,
return found;
}
-bool ED_uvedit_nearest_uv(
- const Scene *scene, Object *obedit, const float co[2], float *dist_sq, float r_uv[2])
+static bool uvedit_nearest_uv(const Scene *scene,
+ Object *obedit,
+ const float co[2],
+ const float scale[2],
+ const bool ignore_selected,
+ float *dist_sq,
+ float r_uv[2])
{
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMIter iter;
@@ -1035,8 +1040,14 @@ bool ED_uvedit_nearest_uv(
BMLoop *l_iter, *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
do {
+ if (ignore_selected && uvedit_uv_select_test(scene, l_iter, cd_loop_uv_offset)) {
+ continue;
+ }
+
const float *uv = ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset))->uv;
- const float dist_test = len_squared_v2v2(co, uv);
+ float co_tmp[2];
+ mul_v2_v2v2(co_tmp, scale, uv);
+ const float dist_test = len_squared_v2v2(co, co_tmp);
if (dist_best > dist_test) {
dist_best = dist_test;
uv_best = uv;
@@ -1052,17 +1063,27 @@ bool ED_uvedit_nearest_uv(
return false;
}
-bool ED_uvedit_nearest_uv_multi(const Scene *scene,
+bool ED_uvedit_nearest_uv_multi(const View2D *v2d,
+ const Scene *scene,
Object **objects,
const uint objects_len,
- const float co[2],
+ const int mval[2],
+ const bool ignore_selected,
float *dist_sq,
float r_uv[2])
{
bool found = false;
+
+ float scale[2], offset[2];
+ UI_view2d_scale_get(v2d, &scale[0], &scale[1]);
+ UI_view2d_view_to_region_fl(v2d, 0.0f, 0.0f, &offset[0], &offset[1]);
+
+ float co[2];
+ sub_v2_v2v2(co, (float[2]){UNPACK2(mval)}, offset);
+
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
- if (ED_uvedit_nearest_uv(scene, obedit, co, dist_sq, r_uv)) {
+ if (uvedit_nearest_uv(scene, obedit, co, scale, ignore_selected, dist_sq, r_uv)) {
found = true;
}
}
diff --git a/source/blender/freestyle/intern/geometry/SweepLine.h b/source/blender/freestyle/intern/geometry/SweepLine.h
index 1165e1bf064..c170ee4d122 100644
--- a/source/blender/freestyle/intern/geometry/SweepLine.h
+++ b/source/blender/freestyle/intern/geometry/SweepLine.h
@@ -183,7 +183,7 @@ template<class T1, class T2> struct binary_rule {
binary_rule()
{
}
- template<class T3, class T4> binary_rule(const binary_rule<T3, T4> &brother)
+ template<class T3, class T4> binary_rule(const binary_rule<T3, T4> & /*brother*/)
{
}
virtual ~binary_rule()
diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt
index 8716d6c8f67..010c327d482 100644
--- a/source/blender/geometry/CMakeLists.txt
+++ b/source/blender/geometry/CMakeLists.txt
@@ -16,15 +16,19 @@ set(INC
set(SRC
intern/mesh_merge_by_distance.cc
+ intern/mesh_primitive_cuboid.cc
intern/mesh_to_curve_convert.cc
intern/point_merge_by_distance.cc
intern/realize_instances.cc
+ intern/resample_curves.cc
intern/uv_parametrizer.c
GEO_mesh_merge_by_distance.hh
+ GEO_mesh_primitive_cuboid.hh
GEO_mesh_to_curve.hh
GEO_point_merge_by_distance.hh
GEO_realize_instances.hh
+ GEO_resample_curves.hh
GEO_uv_parametrizer.h
)
diff --git a/source/blender/geometry/GEO_mesh_primitive_cuboid.hh b/source/blender/geometry/GEO_mesh_primitive_cuboid.hh
new file mode 100644
index 00000000000..d8f16065e2b
--- /dev/null
+++ b/source/blender/geometry/GEO_mesh_primitive_cuboid.hh
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+struct Mesh;
+struct float3;
+namespace blender {
+namespace bke {
+class AttributeIDRef;
+}
+} // namespace blender
+
+namespace blender::geometry {
+
+Mesh *create_cuboid_mesh(
+ const float3 &size, int verts_x, int verts_y, int verts_z, const bke::AttributeIDRef &uv_id);
+
+} // namespace blender::geometry
diff --git a/source/blender/geometry/GEO_resample_curves.hh b/source/blender/geometry/GEO_resample_curves.hh
new file mode 100644
index 00000000000..97399ccb0a5
--- /dev/null
+++ b/source/blender/geometry/GEO_resample_curves.hh
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "FN_field.hh"
+
+#include "BKE_geometry_set.hh"
+
+struct Curves;
+
+namespace blender::geometry {
+
+/**
+ * Create new curves where the selected curves have been resampled with a number of uniform-length
+ * samples defined by the count field. Interpolate attributes to the result, with an accuracy that
+ * depends on the curve's resolution parameter.
+ *
+ * \note The values provided by the #count_field are clamped to 1 or greater.
+ */
+Curves *resample_to_count(const CurveComponent &src_component,
+ const fn::Field<bool> &selection_field,
+ const fn::Field<int> &count_field);
+
+/**
+ * Create new curves resampled to make each segment have the length specified by the
+ * #segment_length field input, rounded to make the length of each segment the same.
+ * The accuracy will depend on the curve's resolution parameter.
+ */
+Curves *resample_to_length(const CurveComponent &src_component,
+ const fn::Field<bool> &selection_field,
+ const fn::Field<float> &segment_length_field);
+
+/**
+ * Evaluate each selected curve to its implicit evaluated points.
+ */
+Curves *resample_to_evaluated(const CurveComponent &src_component,
+ const fn::Field<bool> &selection_field);
+
+} // namespace blender::geometry
diff --git a/source/blender/geometry/intern/mesh_primitive_cuboid.cc b/source/blender/geometry/intern/mesh_primitive_cuboid.cc
new file mode 100644
index 00000000000..e41516d0486
--- /dev/null
+++ b/source/blender/geometry/intern/mesh_primitive_cuboid.cc
@@ -0,0 +1,423 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_index_range.hh"
+#include "BLI_math_vector.h"
+#include "BLI_math_vector.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_attribute_access.hh"
+#include "BKE_geometry_set.hh"
+#include "BKE_mesh.h"
+
+#include "GEO_mesh_primitive_cuboid.hh"
+
+namespace blender::geometry {
+
+struct CuboidConfig {
+ float3 size;
+ int verts_x;
+ int verts_y;
+ int verts_z;
+ int edges_x;
+ int edges_y;
+ int edges_z;
+ int vertex_count;
+ int poly_count;
+ int loop_count;
+
+ CuboidConfig(float3 size, int verts_x, int verts_y, int verts_z)
+ : size(size),
+ verts_x(verts_x),
+ verts_y(verts_y),
+ verts_z(verts_z),
+ edges_x(verts_x - 1),
+ edges_y(verts_y - 1),
+ edges_z(verts_z - 1)
+ {
+ BLI_assert(edges_x > 0 && edges_y > 0 && edges_z > 0);
+ this->vertex_count = this->get_vertex_count();
+ this->poly_count = this->get_poly_count();
+ this->loop_count = this->poly_count * 4;
+ }
+
+ private:
+ int get_vertex_count()
+ {
+ const int inner_position_count = (verts_x - 2) * (verts_y - 2) * (verts_z - 2);
+ return verts_x * verts_y * verts_z - inner_position_count;
+ }
+
+ int get_poly_count()
+ {
+ return 2 * (edges_x * edges_y + edges_y * edges_z + edges_z * edges_x);
+ }
+};
+
+static void calculate_vertices(const CuboidConfig &config, MutableSpan<MVert> verts)
+{
+ const float z_bottom = -config.size.z / 2.0f;
+ const float z_delta = config.size.z / config.edges_z;
+
+ const float x_left = -config.size.x / 2.0f;
+ const float x_delta = config.size.x / config.edges_x;
+
+ const float y_front = -config.size.y / 2.0f;
+ const float y_delta = config.size.y / config.edges_y;
+
+ int vert_index = 0;
+
+ for (const int z : IndexRange(config.verts_z)) {
+ if (ELEM(z, 0, config.edges_z)) {
+ /* Fill bottom and top. */
+ const float z_pos = z_bottom + z_delta * z;
+ for (const int y : IndexRange(config.verts_y)) {
+ const float y_pos = y_front + y_delta * y;
+ for (const int x : IndexRange(config.verts_x)) {
+ const float x_pos = x_left + x_delta * x;
+ copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos));
+ }
+ }
+ }
+ else {
+ for (const int y : IndexRange(config.verts_y)) {
+ if (ELEM(y, 0, config.edges_y)) {
+ /* Fill y-sides. */
+ const float y_pos = y_front + y_delta * y;
+ const float z_pos = z_bottom + z_delta * z;
+ for (const int x : IndexRange(config.verts_x)) {
+ const float x_pos = x_left + x_delta * x;
+ copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos));
+ }
+ }
+ else {
+ /* Fill x-sides. */
+ const float x_pos = x_left;
+ const float y_pos = y_front + y_delta * y;
+ const float z_pos = z_bottom + z_delta * z;
+ copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos));
+ const float x_pos2 = x_left + x_delta * config.edges_x;
+ copy_v3_v3(verts[vert_index++].co, float3(x_pos2, y_pos, z_pos));
+ }
+ }
+ }
+ }
+}
+
+/* vert_1 = bottom left, vert_2 = bottom right, vert_3 = top right, vert_4 = top left.
+ * Hence they are passed as 1,4,3,2 when calculating polys clockwise, and 1,2,3,4 for
+ * anti-clockwise.
+ */
+static void define_quad(MutableSpan<MPoly> polys,
+ MutableSpan<MLoop> loops,
+ const int poly_index,
+ const int loop_index,
+ const int vert_1,
+ const int vert_2,
+ const int vert_3,
+ const int vert_4)
+{
+ MPoly &poly = polys[poly_index];
+ poly.loopstart = loop_index;
+ poly.totloop = 4;
+
+ MLoop &loop_1 = loops[loop_index];
+ loop_1.v = vert_1;
+ MLoop &loop_2 = loops[loop_index + 1];
+ loop_2.v = vert_2;
+ MLoop &loop_3 = loops[loop_index + 2];
+ loop_3.v = vert_3;
+ MLoop &loop_4 = loops[loop_index + 3];
+ loop_4.v = vert_4;
+}
+
+static void calculate_polys(const CuboidConfig &config,
+ MutableSpan<MPoly> polys,
+ MutableSpan<MLoop> loops)
+{
+ int loop_index = 0;
+ int poly_index = 0;
+
+ /* Number of vertices in an XY cross-section of the cube (barring top and bottom faces). */
+ const int xy_cross_section_vert_count = config.verts_x * config.verts_y -
+ (config.verts_x - 2) * (config.verts_y - 2);
+
+ /* Calculate polys for Bottom faces. */
+ int vert_1_start = 0;
+
+ for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) {
+ for (const int x : IndexRange(config.edges_x)) {
+ const int vert_1 = vert_1_start + x;
+ const int vert_2 = vert_1_start + config.verts_x + x;
+ const int vert_3 = vert_2 + 1;
+ const int vert_4 = vert_1 + 1;
+
+ define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4);
+ loop_index += 4;
+ poly_index++;
+ }
+ vert_1_start += config.verts_x;
+ }
+
+ /* Calculate polys for Front faces. */
+ vert_1_start = 0;
+ int vert_2_start = config.verts_x * config.verts_y;
+
+ for ([[maybe_unused]] const int z : IndexRange(config.edges_z)) {
+ for (const int x : IndexRange(config.edges_x)) {
+ define_quad(polys,
+ loops,
+ poly_index,
+ loop_index,
+ vert_1_start + x,
+ vert_1_start + x + 1,
+ vert_2_start + x + 1,
+ vert_2_start + x);
+ loop_index += 4;
+ poly_index++;
+ }
+ vert_1_start = vert_2_start;
+ vert_2_start += config.verts_x * config.verts_y - (config.verts_x - 2) * (config.verts_y - 2);
+ }
+
+ /* Calculate polys for Top faces. */
+ vert_1_start = config.verts_x * config.verts_y +
+ (config.verts_z - 2) * (config.verts_x * config.verts_y -
+ (config.verts_x - 2) * (config.verts_y - 2));
+ vert_2_start = vert_1_start + config.verts_x;
+
+ for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) {
+ for (const int x : IndexRange(config.edges_x)) {
+ define_quad(polys,
+ loops,
+ poly_index,
+ loop_index,
+ vert_1_start + x,
+ vert_1_start + x + 1,
+ vert_2_start + x + 1,
+ vert_2_start + x);
+ loop_index += 4;
+ poly_index++;
+ }
+ vert_2_start += config.verts_x;
+ vert_1_start += config.verts_x;
+ }
+
+ /* Calculate polys for Back faces. */
+ vert_1_start = config.verts_x * config.edges_y;
+ vert_2_start = vert_1_start + xy_cross_section_vert_count;
+
+ for (const int z : IndexRange(config.edges_z)) {
+ if (z == (config.edges_z - 1)) {
+ vert_2_start += (config.verts_x - 2) * (config.verts_y - 2);
+ }
+ for (const int x : IndexRange(config.edges_x)) {
+ define_quad(polys,
+ loops,
+ poly_index,
+ loop_index,
+ vert_1_start + x,
+ vert_2_start + x,
+ vert_2_start + x + 1,
+ vert_1_start + x + 1);
+ loop_index += 4;
+ poly_index++;
+ }
+ vert_2_start += xy_cross_section_vert_count;
+ vert_1_start += xy_cross_section_vert_count;
+ }
+
+ /* Calculate polys for Left faces. */
+ vert_1_start = 0;
+ vert_2_start = config.verts_x * config.verts_y;
+
+ for (const int z : IndexRange(config.edges_z)) {
+ for (const int y : IndexRange(config.edges_y)) {
+ int vert_1;
+ int vert_2;
+ int vert_3;
+ int vert_4;
+
+ if (z == 0 || y == 0) {
+ vert_1 = vert_1_start + config.verts_x * y;
+ vert_4 = vert_1 + config.verts_x;
+ }
+ else {
+ vert_1 = vert_1_start + 2 * y;
+ vert_1 += config.verts_x - 2;
+ vert_4 = vert_1 + 2;
+ }
+
+ if (y == 0 || z == (config.edges_z - 1)) {
+ vert_2 = vert_2_start + config.verts_x * y;
+ vert_3 = vert_2 + config.verts_x;
+ }
+ else {
+ vert_2 = vert_2_start + 2 * y;
+ vert_2 += config.verts_x - 2;
+ vert_3 = vert_2 + 2;
+ }
+
+ define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4);
+ loop_index += 4;
+ poly_index++;
+ }
+ if (z == 0) {
+ vert_1_start += config.verts_x * config.verts_y;
+ }
+ else {
+ vert_1_start += xy_cross_section_vert_count;
+ }
+ vert_2_start += xy_cross_section_vert_count;
+ }
+
+ /* Calculate polys for Right faces. */
+ vert_1_start = config.edges_x;
+ vert_2_start = vert_1_start + config.verts_x * config.verts_y;
+
+ for (const int z : IndexRange(config.edges_z)) {
+ for (const int y : IndexRange(config.edges_y)) {
+ int vert_1 = vert_1_start;
+ int vert_2 = vert_2_start;
+ int vert_3 = vert_2_start + 2;
+ int vert_4 = vert_1 + config.verts_x;
+
+ if (z == 0) {
+ vert_1 = vert_1_start + config.verts_x * y;
+ vert_4 = vert_1 + config.verts_x;
+ }
+ else {
+ vert_1 = vert_1_start + 2 * y;
+ vert_4 = vert_1 + 2;
+ }
+
+ if (z == (config.edges_z - 1)) {
+ vert_2 = vert_2_start + config.verts_x * y;
+ vert_3 = vert_2 + config.verts_x;
+ }
+ else {
+ vert_2 = vert_2_start + 2 * y;
+ vert_3 = vert_2 + 2;
+ }
+
+ if (y == (config.edges_y - 1)) {
+ vert_3 = vert_2 + config.verts_x;
+ vert_4 = vert_1 + config.verts_x;
+ }
+
+ define_quad(polys, loops, poly_index, loop_index, vert_1, vert_4, vert_3, vert_2);
+ loop_index += 4;
+ poly_index++;
+ }
+ if (z == 0) {
+ vert_1_start += config.verts_x * config.verts_y;
+ }
+ else {
+ vert_1_start += xy_cross_section_vert_count;
+ }
+ vert_2_start += xy_cross_section_vert_count;
+ }
+}
+
+static void calculate_uvs(const CuboidConfig &config, Mesh *mesh, const bke::AttributeIDRef &uv_id)
+{
+ MeshComponent mesh_component;
+ mesh_component.replace(mesh, GeometryOwnershipType::Editable);
+ bke::OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>(uv_id, ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
+
+ int loop_index = 0;
+
+ const float x_delta = 0.25f / static_cast<float>(config.edges_x);
+ const float y_delta = 0.25f / static_cast<float>(config.edges_y);
+ const float z_delta = 0.25f / static_cast<float>(config.edges_z);
+
+ /* Calculate bottom face UVs. */
+ for (const int y : IndexRange(config.edges_y)) {
+ for (const int x : IndexRange(config.edges_x)) {
+ uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - y * y_delta);
+ uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - (y + 1) * y_delta);
+ uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - (y + 1) * y_delta);
+ uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - y * y_delta);
+ }
+ }
+
+ /* Calculate front face UVs. */
+ for (const int z : IndexRange(config.edges_z)) {
+ for (const int x : IndexRange(config.edges_x)) {
+ uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + z * z_delta);
+ uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + z * z_delta);
+ uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + (z + 1) * z_delta);
+ uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + (z + 1) * z_delta);
+ }
+ }
+
+ /* Calculate top face UVs. */
+ for (const int y : IndexRange(config.edges_y)) {
+ for (const int x : IndexRange(config.edges_x)) {
+ uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + y * y_delta);
+ uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + y * y_delta);
+ uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + (y + 1) * y_delta);
+ uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + (y + 1) * y_delta);
+ }
+ }
+
+ /* Calculate back face UVs. */
+ for (const int z : IndexRange(config.edges_z)) {
+ for (const int x : IndexRange(config.edges_x)) {
+ uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + z * z_delta);
+ uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + (z + 1) * z_delta);
+ uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + (z + 1) * z_delta);
+ uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + z * z_delta);
+ }
+ }
+
+ /* Calculate left face UVs. */
+ for (const int z : IndexRange(config.edges_z)) {
+ for (const int y : IndexRange(config.edges_y)) {
+ uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + z * z_delta);
+ uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + (z + 1) * z_delta);
+ uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + (z + 1) * z_delta);
+ uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + z * z_delta);
+ }
+ }
+
+ /* Calculate right face UVs. */
+ for (const int z : IndexRange(config.edges_z)) {
+ for (const int y : IndexRange(config.edges_y)) {
+ uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + z * z_delta);
+ uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + z * z_delta);
+ uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + (z + 1) * z_delta);
+ uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + (z + 1) * z_delta);
+ }
+ }
+
+ uv_attribute.save();
+}
+
+Mesh *create_cuboid_mesh(const float3 &size,
+ const int verts_x,
+ const int verts_y,
+ const int verts_z,
+ const bke::AttributeIDRef &uv_id)
+{
+ const CuboidConfig config(size, verts_x, verts_y, verts_z);
+
+ Mesh *mesh = BKE_mesh_new_nomain(
+ config.vertex_count, 0, 0, config.loop_count, config.poly_count);
+
+ calculate_vertices(config, {mesh->mvert, mesh->totvert});
+
+ calculate_polys(config, {mesh->mpoly, mesh->totpoly}, {mesh->mloop, mesh->totloop});
+ BKE_mesh_calc_edges(mesh, false, false);
+
+ if (uv_id) {
+ calculate_uvs(config, mesh, uv_id);
+ }
+
+ return mesh;
+}
+
+} // namespace blender::geometry
diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc
index f3f0e5b1fce..ae07e817c67 100644
--- a/source/blender/geometry/intern/realize_instances.cc
+++ b/source/blender/geometry/intern/realize_instances.cc
@@ -374,7 +374,7 @@ static Vector<std::pair<int, GSpan>> prepare_attribute_fallbacks(
}
/* Convert the attribute on the instances component to the expected attribute type. */
std::unique_ptr<GArray<>> temporary_array = std::make_unique<GArray<>>(
- to_type, instances_component.instances_amount());
+ to_type, instances_component.instances_num());
conversions.convert_to_initialized_n(span, temporary_array->as_mutable_span());
span = temporary_array->as_span();
gather_info.r_temporary_arrays.append(std::move(temporary_array));
@@ -548,7 +548,7 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
case GEO_COMPONENT_TYPE_CURVE: {
const CurveComponent &curve_component = *static_cast<const CurveComponent *>(component);
const Curves *curves = curve_component.get_for_read();
- if (curves != nullptr && curves->geometry.curve_size > 0) {
+ if (curves != nullptr && curves->geometry.curve_num > 0) {
const int curve_index = gather_info.curves.order.index_of(curves);
const RealizeCurveInfo &curve_info = gather_info.curves.realize_info[curve_index];
gather_info.r_tasks.curve_tasks.append({gather_info.r_offsets.curves_offsets,
@@ -556,8 +556,8 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
base_transform,
base_instance_context.curves,
base_instance_context.id});
- gather_info.r_offsets.curves_offsets.point += curves->geometry.point_size;
- gather_info.r_offsets.curves_offsets.curve += curves->geometry.curve_size;
+ gather_info.r_offsets.curves_offsets.point += curves->geometry.point_num;
+ gather_info.r_offsets.curves_offsets.curve += curves->geometry.curve_num;
}
break;
}
@@ -1052,7 +1052,7 @@ static void gather_curves_to_realize(const GeometrySet &geometry_set,
VectorSet<const Curves *> &r_curves)
{
if (const Curves *curves = geometry_set.get_curves_for_read()) {
- if (curves->geometry.curve_size != 0) {
+ if (curves->geometry.curve_num != 0) {
r_curves.add(curves);
}
}
@@ -1215,13 +1215,13 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
const RealizeCurveTask &last_task = tasks.last();
const Curves &last_curves = *last_task.curve_info->curves;
- const int points_size = last_task.start_indices.point + last_curves.geometry.point_size;
- const int curves_size = last_task.start_indices.curve + last_curves.geometry.curve_size;
+ const int points_num = last_task.start_indices.point + last_curves.geometry.point_num;
+ const int curves_num = last_task.start_indices.curve + last_curves.geometry.curve_num;
/* Allocate new curves data-block. */
- Curves *dst_curves_id = bke::curves_new_nomain(points_size, curves_size);
+ Curves *dst_curves_id = bke::curves_new_nomain(points_num, curves_num);
bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry);
- dst_curves.offsets_for_write().last() = points_size;
+ dst_curves.offsets_for_write().last() = points_num;
CurveComponent &dst_component = r_realized_geometry.get_component_for_write<CurveComponent>();
dst_component.replace(dst_curves_id);
diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc
new file mode 100644
index 00000000000..7895225a189
--- /dev/null
+++ b/source/blender/geometry/intern/resample_curves.cc
@@ -0,0 +1,474 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_length_parameterize.hh"
+#include "BLI_task.hh"
+
+#include "FN_field.hh"
+#include "FN_multi_function_builder.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_curves.hh"
+#include "BKE_curves_utils.hh"
+#include "BKE_geometry_fields.hh"
+
+#include "GEO_resample_curves.hh"
+
+namespace blender::geometry {
+
+static fn::Field<int> get_count_input_max_one(const fn::Field<int> &count_field)
+{
+ static fn::CustomMF_SI_SO<int, int> max_one_fn(
+ "Clamp Above One",
+ [](int value) { return std::max(1, value); },
+ fn::CustomMF_presets::AllSpanOrSingle());
+ auto clamp_op = std::make_shared<fn::FieldOperation>(
+ fn::FieldOperation(max_one_fn, {count_field}));
+
+ return fn::Field<int>(std::move(clamp_op));
+}
+
+static fn::Field<int> get_count_input_from_length(const fn::Field<float> &length_field)
+{
+ static fn::CustomMF_SI_SI_SO<float, float, int> get_count_fn(
+ "Length Input to Count",
+ [](const float curve_length, const float sample_length) {
+ /* Find the number of sampled segments by dividing the total length by
+ * the sample length. Then there is one more sampled point than segment. */
+ const int count = int(curve_length / sample_length) + 1;
+ return std::max(1, count);
+ },
+ fn::CustomMF_presets::AllSpanOrSingle());
+
+ auto get_count_op = std::make_shared<fn::FieldOperation>(fn::FieldOperation(
+ get_count_fn,
+ {fn::Field<float>(std::make_shared<bke::CurveLengthFieldInput>()), length_field}));
+
+ return fn::Field<int>(std::move(get_count_op));
+}
+
+/**
+ * Return true if the attribute should be copied/interpolated to the result curves.
+ * Don't output attributes that correspond to curve types that have no curves in the result.
+ */
+static bool interpolate_attribute_to_curves(const bke::AttributeIDRef &attribute_id,
+ const std::array<int, CURVE_TYPES_NUM> &type_counts)
+{
+ if (!attribute_id.is_named()) {
+ return true;
+ }
+ if (ELEM(attribute_id.name(),
+ "handle_type_left",
+ "handle_type_right",
+ "handle_left",
+ "handle_right")) {
+ return type_counts[CURVE_TYPE_BEZIER] != 0;
+ }
+ if (ELEM(attribute_id.name(), "nurbs_weight")) {
+ return type_counts[CURVE_TYPE_NURBS] != 0;
+ }
+ return true;
+}
+
+/**
+ * Return true if the attribute should be copied to poly curves.
+ */
+static bool interpolate_attribute_to_poly_curve(const bke::AttributeIDRef &attribute_id)
+{
+ static const Set<StringRef> no_interpolation{{
+ "handle_type_left",
+ "handle_type_right",
+ "handle_position_right",
+ "handle_position_left",
+ "nurbs_weight",
+ }};
+ return !(attribute_id.is_named() && no_interpolation.contains(attribute_id.name()));
+}
+
+/**
+ * Retrieve spans from source and result attributes.
+ */
+static void retrieve_attribute_spans(const Span<bke::AttributeIDRef> ids,
+ const CurveComponent &src_component,
+ CurveComponent &dst_component,
+ Vector<GSpan> &src,
+ Vector<GMutableSpan> &dst,
+ Vector<bke::OutputAttribute> &dst_attributes)
+{
+ for (const int i : ids.index_range()) {
+ GVArray src_attribute = src_component.attribute_try_get_for_read(ids[i], ATTR_DOMAIN_POINT);
+ BLI_assert(src_attribute);
+ src.append(src_attribute.get_internal_span());
+
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(src_attribute.type());
+ bke::OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ ids[i], ATTR_DOMAIN_POINT, data_type);
+ dst.append(dst_attribute.as_span());
+ dst_attributes.append(std::move(dst_attribute));
+ }
+}
+
+struct AttributesForInterpolation : NonCopyable, NonMovable {
+ Vector<GSpan> src;
+ Vector<GMutableSpan> dst;
+
+ Vector<bke::OutputAttribute> dst_attributes;
+
+ Vector<GSpan> src_no_interpolation;
+ Vector<GMutableSpan> dst_no_interpolation;
+};
+
+/**
+ * Gather a set of all generic attribute IDs to copy to the result curves.
+ */
+static void gather_point_attributes_to_interpolate(const CurveComponent &src_component,
+ CurveComponent &dst_component,
+ AttributesForInterpolation &result)
+{
+ bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(
+ dst_component.get_for_write()->geometry);
+
+ VectorSet<bke::AttributeIDRef> ids;
+ VectorSet<bke::AttributeIDRef> ids_no_interpolation;
+ src_component.attribute_foreach(
+ [&](const bke::AttributeIDRef &id, const AttributeMetaData meta_data) {
+ if (meta_data.domain != ATTR_DOMAIN_POINT) {
+ return true;
+ }
+ if (!interpolate_attribute_to_curves(id, dst_curves.curve_type_counts())) {
+ return true;
+ }
+ if (interpolate_attribute_to_poly_curve(id)) {
+ ids.add_new(id);
+ }
+ else {
+ ids_no_interpolation.add_new(id);
+ }
+ return true;
+ });
+
+ /* Position is handled differently since it has non-generic interpolation for Bezier
+ * curves and because the evaluated positions are cached for each evaluated point. */
+ ids.remove_contained("position");
+
+ retrieve_attribute_spans(
+ ids, src_component, dst_component, result.src, result.dst, result.dst_attributes);
+
+ /* Attributes that aren't interpolated like Bezier handles still have to be be copied
+ * to the result when there are any unselected curves of the corresponding type. */
+ retrieve_attribute_spans(ids_no_interpolation,
+ src_component,
+ dst_component,
+ result.src_no_interpolation,
+ result.dst_no_interpolation,
+ result.dst_attributes);
+
+ dst_curves.update_customdata_pointers();
+}
+
+/**
+ * Copy the provided point attribute values between all curves in the #curve_ranges index
+ * ranges, assuming that all curves are the same size in #src_curves and #dst_curves.
+ */
+template<typename T>
+static void copy_between_curves(const bke::CurvesGeometry &src_curves,
+ const bke::CurvesGeometry &dst_curves,
+ const Span<IndexRange> curve_ranges,
+ const Span<T> src,
+ const MutableSpan<T> dst)
+{
+ threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) {
+ for (const IndexRange range : curve_ranges.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curves(range);
+ const IndexRange dst_points = dst_curves.points_for_curves(range);
+ /* The arrays might be large, so a threaded copy might make sense here too. */
+ dst.slice(dst_points).copy_from(src.slice(src_points));
+ }
+ });
+}
+static void copy_between_curves(const bke::CurvesGeometry &src_curves,
+ const bke::CurvesGeometry &dst_curves,
+ const Span<IndexRange> unselected_ranges,
+ const GSpan src,
+ const GMutableSpan dst)
+{
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ copy_between_curves(src_curves, dst_curves, unselected_ranges, src.typed<T>(), dst.typed<T>());
+ });
+}
+
+static Curves *resample_to_uniform(const CurveComponent &src_component,
+ const fn::Field<bool> &selection_field,
+ const fn::Field<int> &count_field)
+{
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_component.get_for_read()->geometry);
+
+ /* Create the new curves without any points and evaluate the final count directly
+ * into the offsets array, in order to be accumulated into offsets later. */
+ Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num());
+ bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry);
+
+ /* Directly copy curve attributes, since they stay the same (except for curve types). */
+ CustomData_copy(&src_curves.curve_data,
+ &dst_curves.curve_data,
+ CD_MASK_ALL,
+ CD_DUPLICATE,
+ src_curves.curves_num());
+ MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
+
+ bke::GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
+ fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
+ evaluator.set_selection(selection_field);
+ evaluator.add_with_destination(count_field, dst_offsets);
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+ const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
+ src_curves.curves_range(), nullptr);
+
+ /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */
+ bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets);
+ bke::curves::accumulate_counts_to_offsets(dst_offsets);
+ dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
+
+ /* All resampled curves are poly curves. */
+ dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY);
+
+ VArray<bool> curves_cyclic = src_curves.cyclic();
+ VArray<int8_t> curve_types = src_curves.curve_types();
+ Span<float3> evaluated_positions = src_curves.evaluated_positions();
+ MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
+
+ AttributesForInterpolation attributes;
+ CurveComponent dst_component;
+ dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable);
+ gather_point_attributes_to_interpolate(src_component, dst_component, attributes);
+
+ src_curves.ensure_evaluated_lengths();
+
+ /* Sampling arbitrary attributes works by first interpolating them to the curve's standard
+ * "evaluated points" and then interpolating that result with the uniform samples. This is
+ * potentially wasteful when down-sampling a curve to many fewer points. There are two possible
+ * solutions: only sample the necessary points for interpolation, or first sample curve
+ * parameter/segment indices and evaluate the curve directly. */
+ Array<int> sample_indices(dst_curves.points_num());
+ Array<float> sample_factors(dst_curves.points_num());
+
+ /* Use a "for each group of curves: for each attribute: for each curve" pattern to work on
+ * smaller sections of data that ideally fit into CPU cache better than simply one attribute at a
+ * time or one curve at a time. */
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) {
+ const IndexMask sliced_selection = selection.slice(selection_range);
+
+ Vector<std::byte> evaluated_buffer;
+
+ /* Gather uniform samples based on the accumulated lengths of the original curve. */
+ for (const int i_curve : sliced_selection) {
+ const bool cyclic = curves_cyclic[i_curve];
+ const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
+ length_parameterize::create_uniform_samples(
+ src_curves.evaluated_lengths_for_curve(i_curve, cyclic),
+ curves_cyclic[i_curve],
+ sample_indices.as_mutable_span().slice(dst_points),
+ sample_factors.as_mutable_span().slice(dst_points));
+ }
+
+ /* For every attribute, evaluate attributes from every curve in the range in the original
+ * curve's "evaluated points", then use linear interpolation to sample to the result. */
+ for (const int i_attribute : attributes.dst.index_range()) {
+ attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ Span<T> src = attributes.src[i_attribute].typed<T>();
+ MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>();
+
+ for (const int i_curve : sliced_selection) {
+ const IndexRange src_points = src_curves.points_for_curve(i_curve);
+ const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
+
+ if (curve_types[i_curve] == CURVE_TYPE_POLY) {
+ length_parameterize::linear_interpolation(src.slice(src_points),
+ sample_indices.as_span().slice(dst_points),
+ sample_factors.as_span().slice(dst_points),
+ dst.slice(dst_points));
+ }
+ else {
+ const int evaluated_size = src_curves.evaluated_points_for_curve(i_curve).size();
+ evaluated_buffer.clear();
+ evaluated_buffer.resize(sizeof(T) * evaluated_size);
+ MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>();
+ src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated);
+
+ length_parameterize::linear_interpolation(evaluated.as_span(),
+ sample_indices.as_span().slice(dst_points),
+ sample_factors.as_span().slice(dst_points),
+ dst.slice(dst_points));
+ }
+ }
+ });
+ }
+
+ /* Interpolate the evaluated positions to the resampled curves. */
+ for (const int i_curve : sliced_selection) {
+ const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve);
+ const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
+ length_parameterize::linear_interpolation(evaluated_positions.slice(src_points),
+ sample_indices.as_span().slice(dst_points),
+ sample_factors.as_span().slice(dst_points),
+ dst_positions.slice(dst_points));
+ }
+
+ /* Fill the default value for non-interpolating attributes that still must be copied. */
+ for (GMutableSpan dst : attributes.dst_no_interpolation) {
+ for (const int i_curve : sliced_selection) {
+ const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
+ dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size());
+ }
+ }
+ });
+
+ /* Any attribute data from unselected curve points can be directly copied. */
+ for (const int i : attributes.src.index_range()) {
+ copy_between_curves(
+ src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
+ }
+ for (const int i : attributes.src_no_interpolation.index_range()) {
+ copy_between_curves(src_curves,
+ dst_curves,
+ unselected_ranges,
+ attributes.src_no_interpolation[i],
+ attributes.dst_no_interpolation[i]);
+ }
+
+ /* Copy positions for unselected curves. */
+ Span<float3> src_positions = src_curves.positions();
+ copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions);
+
+ for (bke::OutputAttribute &attribute : attributes.dst_attributes) {
+ attribute.save();
+ }
+
+ return dst_curves_id;
+}
+
+Curves *resample_to_count(const CurveComponent &src_component,
+ const fn::Field<bool> &selection_field,
+ const fn::Field<int> &count_field)
+{
+ return resample_to_uniform(src_component, selection_field, get_count_input_max_one(count_field));
+}
+
+Curves *resample_to_length(const CurveComponent &src_component,
+ const fn::Field<bool> &selection_field,
+ const fn::Field<float> &segment_length_field)
+{
+ return resample_to_uniform(
+ src_component, selection_field, get_count_input_from_length(segment_length_field));
+}
+
+Curves *resample_to_evaluated(const CurveComponent &src_component,
+ const fn::Field<bool> &selection_field)
+{
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_component.get_for_read()->geometry);
+
+ bke::GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
+ fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
+ evaluator.set_selection(selection_field);
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+ const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
+ src_curves.curves_range(), nullptr);
+
+ Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num());
+ bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry);
+
+ /* Directly copy curve attributes, since they stay the same (except for curve types). */
+ CustomData_copy(&src_curves.curve_data,
+ &dst_curves.curve_data,
+ CD_MASK_ALL,
+ CD_DUPLICATE,
+ src_curves.curves_num());
+ /* All resampled curves are poly curves. */
+ dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY);
+ MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
+
+ src_curves.ensure_evaluated_offsets();
+ threading::parallel_for(selection.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ dst_offsets[i] = src_curves.evaluated_points_for_curve(i).size();
+ }
+ });
+ bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets);
+ bke::curves::accumulate_counts_to_offsets(dst_offsets);
+
+ dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
+
+ /* Create the correct number of uniform-length samples for every selected curve. */
+ Span<float3> evaluated_positions = src_curves.evaluated_positions();
+ MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
+
+ AttributesForInterpolation attributes;
+ CurveComponent dst_component;
+ dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable);
+ gather_point_attributes_to_interpolate(src_component, dst_component, attributes);
+
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) {
+ const IndexMask sliced_selection = selection.slice(selection_range);
+
+ /* Evaluate generic point attributes directly to the result attributes. */
+ for (const int i_attribute : attributes.dst.index_range()) {
+ attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ Span<T> src = attributes.src[i_attribute].typed<T>();
+ MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>();
+
+ for (const int i_curve : sliced_selection) {
+ const IndexRange src_points = src_curves.points_for_curve(i_curve);
+ const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
+ src_curves.interpolate_to_evaluated(
+ i_curve, src.slice(src_points), dst.slice(dst_points));
+ }
+ });
+ }
+
+ /* Copy the evaluated positions to the selected curves. */
+ for (const int i_curve : sliced_selection) {
+ const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve);
+ const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
+ dst_positions.slice(dst_points).copy_from(evaluated_positions.slice(src_points));
+ }
+
+ /* Fill the default value for non-interpolating attributes that still must be copied. */
+ for (GMutableSpan dst : attributes.dst_no_interpolation) {
+ for (const int i_curve : sliced_selection) {
+ const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
+ dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size());
+ }
+ }
+ });
+
+ /* Any attribute data from unselected curve points can be directly copied. */
+ for (const int i : attributes.src.index_range()) {
+ copy_between_curves(
+ src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
+ }
+ for (const int i : attributes.src_no_interpolation.index_range()) {
+ copy_between_curves(src_curves,
+ dst_curves,
+ unselected_ranges,
+ attributes.src_no_interpolation[i],
+ attributes.dst_no_interpolation[i]);
+ }
+
+ /* Copy positions for unselected curves. */
+ Span<float3> src_positions = src_curves.positions();
+ copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions);
+
+ for (bke::OutputAttribute &attribute : attributes.dst_attributes) {
+ attribute.save();
+ }
+
+ return dst_curves_id;
+}
+
+} // namespace blender::geometry
diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt
index 6108629183c..69fc26c99e9 100644
--- a/source/blender/gpencil_modifiers/CMakeLists.txt
+++ b/source/blender/gpencil_modifiers/CMakeLists.txt
@@ -65,14 +65,30 @@ set(SRC
# Lineart code
intern/lineart/lineart_chain.c
+ intern/lineart/lineart_cpp_bridge.cc
intern/lineart/lineart_cpu.c
intern/lineart/lineart_ops.c
intern/lineart/lineart_util.c
intern/lineart/MOD_lineart.h
intern/lineart/lineart_intern.h
+)
+
+if(WITH_TBB)
+add_definitions(-DWITH_TBB)
+if(WIN32)
+ # TBB includes Windows.h which will define min/max macros
+ # that will collide with the stl versions.
+ add_definitions(-DNOMINMAX)
+endif()
+list(APPEND INC_SYS
+ ${TBB_INCLUDE_DIRS}
+)
+list(APPEND LIB
+ ${TBB_LIBRARIES}
)
+endif()
set(LIB
)
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
index 1058f861be3..0e7df2a136d 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
@@ -387,7 +387,6 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayout *col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "use_remove_doubles", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "use_edge_overlap", 0, IFACE_("Overlapping Edges As Contour"), ICON_NONE);
uiItemR(col, ptr, "use_object_instances", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE);
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c
index fcf1e28c6da..259e62a249c 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c
@@ -122,6 +122,8 @@ static void deformStroke(GpencilModifierData *md,
const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
const bool invert_group = (mmd->flag & GP_NOISE_INVERT_VGROUP) != 0;
const bool use_curve = (mmd->flag & GP_NOISE_CUSTOM_CURVE) != 0 && mmd->curve_intensity;
+ const int cfra = (int)DEG_get_ctime(depsgraph);
+ const bool is_keyframe = (mmd->noise_mode == GP_NOISE_RANDOM_KEYFRAME);
if (!is_stroke_affected_by_modifier(ob,
mmd->layername,
@@ -148,7 +150,13 @@ static void deformStroke(GpencilModifierData *md,
seed += BLI_hash_string(md->name);
if (mmd->flag & GP_NOISE_USE_RANDOM) {
- seed += ((int)DEG_get_ctime(depsgraph)) / mmd->step;
+ if (!is_keyframe) {
+ seed += cfra / mmd->step;
+ }
+ else {
+ /* If change every keyframe, use the last keyframe. */
+ seed += gpf->framenum;
+ }
}
/* Sanitize as it can create out of bound reads. */
@@ -302,7 +310,12 @@ static void random_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayoutSetActive(layout, RNA_boolean_get(ptr, "use_random"));
- uiItemR(layout, ptr, "step", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "random_mode", 0, NULL, ICON_NONE);
+
+ const int mode = RNA_enum_get(ptr, "random_mode");
+ if (mode != GP_NOISE_RANDOM_KEYFRAME) {
+ uiItemR(layout, ptr, "step", 0, NULL, ICON_NONE);
+ }
}
static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
index 5d952991cf7..99107a96cfe 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
@@ -132,7 +132,7 @@ typedef struct LineartEdge {
char min_occ;
/** Also for line type determination on chaining. */
- unsigned char flags;
+ uint16_t flags;
unsigned char intersection_mask;
/**
@@ -171,7 +171,7 @@ typedef struct LineartEdgeChainItem {
/** For restoring position to 3d space. */
float gpos[3];
float normal[3];
- unsigned char line_type;
+ uint16_t line_type;
char occlusion;
unsigned char material_mask_bits;
unsigned char intersection_mask;
@@ -189,6 +189,12 @@ typedef struct LineartChainRegisterEntry {
char is_left;
} LineartChainRegisterEntry;
+typedef struct LineartAdjacentEdge {
+ unsigned int v1;
+ unsigned int v2;
+ unsigned int e;
+} LineartAdjacentEdge;
+
enum eLineArtTileRecursiveLimit {
/* If tile gets this small, it's already much smaller than a pixel. No need to continue
* splitting. */
@@ -396,7 +402,7 @@ typedef struct LineartObjectInfo {
typedef struct LineartObjectLoadTaskInfo {
struct LineartRenderBuffer *rb;
- struct Depsgraph *dg;
+ int thread_id;
/* LinkNode styled list */
LineartObjectInfo *pending;
/* Used to spread the load across several threads. This can not overflow. */
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc
new file mode 100644
index 00000000000..174399618a5
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup modifiers
+ */
+
+#include "BLI_sort.hh"
+#include "BLI_vector.hh"
+#include "MOD_lineart.h"
+#include "lineart_intern.h"
+
+static bool cmp_adjacent_items(const LineartAdjacentEdge &p1, const LineartAdjacentEdge &p2)
+{
+ int a = p1.v1 - p2.v1;
+ int b = p1.v2 - p2.v2;
+ /* parallel_sort() requires cmp() to return true when the first element needs to appear before
+ * the second element in the sorted array, false otherwise (strict weak ordering), see
+ * https://en.cppreference.com/w/cpp/named_req/Compare. */
+ return a < 0 ? true : (a == 0 ? b < 0 : false);
+}
+
+void lineart_sort_adjacent_items(LineartAdjacentEdge *ai, int length)
+{
+ blender::parallel_sort(ai, ai + length - 1, cmp_adjacent_items);
+}
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
index 24e11f6be3b..b09bb15ce81 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -8,6 +8,7 @@
#include "MOD_gpencil_lineart.h"
#include "MOD_lineart.h"
+#include "BLI_edgehash.h"
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@@ -28,6 +29,8 @@
#include "BKE_lib_id.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
+#include "BKE_mesh_runtime.h"
#include "BKE_object.h"
#include "BKE_pointcache.h"
#include "BKE_scene.h"
@@ -1426,17 +1429,45 @@ static void lineart_main_discard_out_of_frame_edges(LineartRenderBuffer *rb)
}
}
-/**
- * Transform a single vert to it's viewing position.
- */
-static void lineart_vert_transform(
- BMVert *v, int index, LineartVert *RvBuf, double (*mv_mat)[4], double (*mvp_mat)[4])
+typedef struct LineartEdgeNeighbor {
+ int e;
+ uint16_t flags;
+ int v1, v2;
+} LineartEdgeNeighbor;
+
+typedef struct VertData {
+ MVert *mvert;
+ LineartVert *v_arr;
+ double (*model_view)[4];
+ double (*model_view_proj)[4];
+} VertData;
+
+static void lineart_mvert_transform_task(void *__restrict userdata,
+ const int i,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
+ VertData *vert_task_data = (VertData *)userdata;
+ MVert *m_v = &vert_task_data->mvert[i];
double co[4];
- LineartVert *vt = &RvBuf[index];
- copy_v3db_v3fl(co, v->co);
- mul_v3_m4v3_db(vt->gloc, mv_mat, co);
- mul_v4_m4v3_db(vt->fbcoord, mvp_mat, co);
+ LineartVert *v = &vert_task_data->v_arr[i];
+ copy_v3db_v3fl(co, m_v->co);
+ mul_v3_m4v3_db(v->gloc, vert_task_data->model_view, co);
+ mul_v4_m4v3_db(v->fbcoord, vert_task_data->model_view_proj, co);
+ v->index = i;
+}
+
+#define LRT_EDGE_FLAG_TYPE_MAX_BITS 6
+
+static int lineart_edge_type_duplication_count(char eflag)
+{
+ int count = 0;
+ /* See eLineartEdgeFlag for details. */
+ for (int i = 0; i < LRT_EDGE_FLAG_TYPE_MAX_BITS; i++) {
+ if (eflag & (1 << i)) {
+ count++;
+ }
+ }
+ return count;
}
/**
@@ -1452,88 +1483,123 @@ static LineartTriangle *lineart_triangle_from_index(LineartRenderBuffer *rb,
return (LineartTriangle *)b;
}
-static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb,
- BMEdge *e,
- LineartTriangle *rt_array,
- LineartVert *rv_array,
- float crease_threshold,
- bool use_auto_smooth,
- bool use_freestyle_edge,
- bool use_freestyle_face,
- BMesh *bm_if_freestyle)
+typedef struct EdgeFeatData {
+ LineartRenderBuffer *rb;
+ Mesh *me;
+ const MLoopTri *mlooptri;
+ LineartTriangle *tri_array;
+ LineartVert *v_array;
+ float crease_threshold;
+ bool use_auto_smooth;
+ bool use_freestyle_face;
+ int freestyle_face_index;
+ bool use_freestyle_edge;
+ int freestyle_edge_index;
+ LineartEdgeNeighbor *edge_nabr;
+} EdgeFeatData;
+
+typedef struct EdgeFeatReduceData {
+ int feat_edges;
+} EdgeFeatReduceData;
+
+static void feat_data_sum_reduce(const void *__restrict UNUSED(userdata),
+ void *__restrict chunk_join,
+ void *__restrict chunk)
+{
+ EdgeFeatReduceData *feat_chunk_join = (EdgeFeatReduceData *)chunk_join;
+ EdgeFeatReduceData *feat_chunk = (EdgeFeatReduceData *)chunk;
+ feat_chunk_join->feat_edges += feat_chunk->feat_edges;
+}
+
+static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata,
+ const int i,
+ const TaskParallelTLS *__restrict tls)
{
- BMLoop *ll, *lr = NULL;
+ EdgeFeatData *e_feat_data = (EdgeFeatData *)userdata;
+ EdgeFeatReduceData *reduce_data = (EdgeFeatReduceData *)tls->userdata_chunk;
+ Mesh *me = e_feat_data->me;
+ LineartEdgeNeighbor *edge_nabr = e_feat_data->edge_nabr;
+ const MLoopTri *mlooptri = e_feat_data->mlooptri;
- ll = e->l;
- if (ll) {
- lr = e->l->radial_next;
- }
+ uint16_t edge_flag_result = 0;
- if (!ll && !lr) {
- return LRT_EDGE_FLAG_LOOSE;
+ /* Because the edge neighbor array contains loop edge pairs, we only need to process the first
+ * edge in the pair. Otherwise we would add the same edge that the loops represent twice. */
+ if (i < edge_nabr[i].e) {
+ return;
}
- FreestyleEdge *fel, *fer;
bool face_mark_filtered = false;
- uint16_t edge_flag_result = 0;
+ bool enable_face_mark = (e_feat_data->use_freestyle_face && e_feat_data->rb->filter_face_mark);
bool only_contour = false;
-
- if (use_freestyle_face && rb->filter_face_mark) {
- fel = CustomData_bmesh_get(&bm_if_freestyle->pdata, ll->f->head.data, CD_FREESTYLE_FACE);
- if (ll != lr && lr) {
- fer = CustomData_bmesh_get(&bm_if_freestyle->pdata, lr->f->head.data, CD_FREESTYLE_FACE);
+ if (enable_face_mark) {
+ FreestyleFace *ff1, *ff2;
+ int index = e_feat_data->freestyle_face_index;
+ if (index > -1) {
+ ff1 = &((FreestyleFace *)me->pdata.layers[index].data)[mlooptri[i / 3].poly];
+ }
+ if (edge_nabr[i].e > -1) {
+ ff2 = &((FreestyleFace *)me->pdata.layers[index].data)[mlooptri[edge_nabr[i].e / 3].poly];
}
else {
- /* Handles mesh boundary case */
- fer = fel;
+ /* Handle mesh boundary cases: We want mesh boundaries to respect
+ * `filter_face_mark_boundaries` option the same way as face mark boundaries, and the code
+ * path is simper when it's assuming both ff1 and ff2 not NULL. */
+ ff2 = ff1;
}
- if (rb->filter_face_mark_boundaries ^ rb->filter_face_mark_invert) {
- if ((fel->flag & FREESTYLE_FACE_MARK) || (fer->flag & FREESTYLE_FACE_MARK)) {
+ if (e_feat_data->rb->filter_face_mark_boundaries ^ e_feat_data->rb->filter_face_mark_invert) {
+ if ((ff1->flag & FREESTYLE_FACE_MARK) || (ff2->flag & FREESTYLE_FACE_MARK)) {
face_mark_filtered = true;
}
}
else {
- if ((fel->flag & FREESTYLE_FACE_MARK) && (fer->flag & FREESTYLE_FACE_MARK) && (fer != fel)) {
+ if ((ff1->flag & FREESTYLE_FACE_MARK) && (ff2->flag & FREESTYLE_FACE_MARK) && (ff2 != ff1)) {
face_mark_filtered = true;
}
}
- if (rb->filter_face_mark_invert) {
+ if (e_feat_data->rb->filter_face_mark_invert) {
face_mark_filtered = !face_mark_filtered;
}
if (!face_mark_filtered) {
- if (rb->filter_face_mark_keep_contour) {
+ edge_nabr[i].flags = LRT_EDGE_FLAG_INHIBIT;
+ if (e_feat_data->rb->filter_face_mark_keep_contour) {
only_contour = true;
}
- else {
- return 0;
- }
}
}
+ if (enable_face_mark && !face_mark_filtered && !only_contour) {
+ return;
+ }
+
/* Mesh boundary */
- if (!lr || ll == lr) {
- return (edge_flag_result | LRT_EDGE_FLAG_CONTOUR);
+ if (edge_nabr[i].e == -1) {
+ edge_nabr[i].flags = LRT_EDGE_FLAG_CONTOUR;
+ reduce_data->feat_edges += 1;
+ return;
}
LineartTriangle *tri1, *tri2;
- LineartVert *l;
+ LineartVert *vert;
+ LineartRenderBuffer *rb = e_feat_data->rb;
+
+ int f1 = i / 3, f2 = edge_nabr[i].e / 3;
/* The mesh should already be triangulated now, so we can assume each face is a triangle. */
- tri1 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(ll->f));
- tri2 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(lr->f));
+ tri1 = lineart_triangle_from_index(rb, e_feat_data->tri_array, f1);
+ tri2 = lineart_triangle_from_index(rb, e_feat_data->tri_array, f2);
- l = &rv_array[BM_elem_index_get(e->v1)];
+ vert = &e_feat_data->v_array[edge_nabr[i].v1];
- double vv[3];
- double *view_vector = vv;
+ double view_vector_persp[3];
+ double *view_vector = view_vector_persp;
double dot_1 = 0, dot_2 = 0;
double result;
bool material_back_face = ((tri1->flags | tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING);
if (rb->use_contour || rb->use_back_face_culling || material_back_face) {
-
if (rb->cam_is_persp) {
- sub_v3_v3v3_db(view_vector, rb->camera_pos, l->gloc);
+ sub_v3_v3v3_db(view_vector, rb->camera_pos, vert->gloc);
}
else {
view_vector = rb->view_vector;
@@ -1542,11 +1608,10 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb,
dot_1 = dot_v3v3_db(view_vector, tri1->gn);
dot_2 = dot_v3v3_db(view_vector, tri2->gn);
- if (rb->use_contour && (result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) {
+ if ((result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) {
edge_flag_result |= LRT_EDGE_FLAG_CONTOUR;
}
- /* Because the ray points towards the camera, so back-face is when dot value being negative. */
if (rb->use_back_face_culling) {
if (dot_1 < 0) {
tri1->flags |= LRT_CULL_DISCARD;
@@ -1564,56 +1629,129 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb,
}
}
}
- else {
- view_vector = rb->view_vector;
- }
- if ((result = dot_1 * dot_2) <= 0 && (fabs(dot_1) + fabs(dot_2))) {
- edge_flag_result |= LRT_EDGE_FLAG_CONTOUR;
- }
+ if (!only_contour) {
- /* For when face mark filtering decided that we discard the face but keep_contour option is on.
- * so we still have correct full contour around the object. */
- if (only_contour) {
- return edge_flag_result;
- }
+ if (rb->use_crease) {
+ bool do_crease = true;
+ if (!rb->force_crease && !e_feat_data->use_auto_smooth &&
+ (me->mpoly[mlooptri[f1].poly].flag & ME_SMOOTH) &&
+ (me->mpoly[mlooptri[f2].poly].flag & ME_SMOOTH)) {
+ do_crease = false;
+ }
+ if (do_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < e_feat_data->crease_threshold)) {
+ edge_flag_result |= LRT_EDGE_FLAG_CREASE;
+ }
+ }
+
+ int mat1 = me->mpoly[mlooptri[f1].poly].mat_nr;
+ int mat2 = me->mpoly[mlooptri[f2].poly].mat_nr;
- /* Do not show lines other than contour on back face (because contour has one adjacent face that
- * isn't a back face).
- * TODO(Yiming): Do we need separate option for this? */
- if (rb->use_back_face_culling ||
- ((tri1->flags & tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING)) {
- if (dot_1 < 0 && dot_2 < 0) {
- return edge_flag_result;
+ if (rb->use_material && mat1 != mat2) {
+ edge_flag_result |= LRT_EDGE_FLAG_MATERIAL;
}
}
+ else { /* only_contour */
+ if (!edge_flag_result) { /* Other edge types inhibited */
+ return;
+ }
+ }
+
+ int real_edges[3];
+ BKE_mesh_looptri_get_real_edges(me, &mlooptri[i / 3], real_edges);
+
+ if (real_edges[i % 3] >= 0) {
+ MEdge *medge = &me->medge[real_edges[i % 3]];
- if (rb->use_crease) {
- if (rb->sharp_as_crease && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) {
+ if (rb->use_crease && rb->sharp_as_crease && (medge->flag & ME_SHARP)) {
edge_flag_result |= LRT_EDGE_FLAG_CREASE;
}
- else {
- bool do_crease = true;
- if (!rb->force_crease && !use_auto_smooth &&
- (BM_elem_flag_test(ll->f, BM_ELEM_SMOOTH) && BM_elem_flag_test(lr->f, BM_ELEM_SMOOTH))) {
- do_crease = false;
- }
- if (do_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < crease_threshold)) {
- edge_flag_result |= LRT_EDGE_FLAG_CREASE;
+
+ if (rb->use_edge_marks && e_feat_data->use_freestyle_edge) {
+ FreestyleEdge *fe;
+ int index = e_feat_data->freestyle_edge_index;
+ fe = &((FreestyleEdge *)me->edata.layers[index].data)[real_edges[i % 3]];
+ if (fe->flag & FREESTYLE_EDGE_MARK) {
+ edge_flag_result |= LRT_EDGE_FLAG_EDGE_MARK;
}
}
}
- if (rb->use_material && (ll->f->mat_nr != lr->f->mat_nr)) {
- edge_flag_result |= LRT_EDGE_FLAG_MATERIAL;
+
+ edge_nabr[i].flags = edge_flag_result;
+
+ if (edge_flag_result) {
+ /* Only allocate for feature edge (instead of all edges) to save memory.
+ * If allow duplicated edges, one edge gets added multiple times if it has multiple types.
+ */
+ reduce_data->feat_edges += e_feat_data->rb->allow_duplicated_types ?
+ lineart_edge_type_duplication_count(edge_flag_result) :
+ 1;
}
- if (use_freestyle_edge && rb->use_edge_marks) {
- FreestyleEdge *fe;
- fe = CustomData_bmesh_get(&bm_if_freestyle->edata, e->head.data, CD_FREESTYLE_EDGE);
- if (fe->flag & FREESTYLE_EDGE_MARK) {
- edge_flag_result |= LRT_EDGE_FLAG_EDGE_MARK;
- }
+}
+
+typedef struct LooseEdgeData {
+ int loose_count;
+ int loose_max;
+ MEdge **loose_array;
+ Mesh *me;
+} LooseEdgeData;
+
+static void lineart_loose_data_reallocate(LooseEdgeData *loose_data, int count)
+{
+ MEdge **new_arr = MEM_callocN(sizeof(MEdge *) * count, "loose edge array");
+ if (loose_data->loose_array) {
+ memcpy(new_arr, loose_data->loose_array, sizeof(MEdge *) * loose_data->loose_max);
+ MEM_freeN(loose_data->loose_array);
+ }
+ loose_data->loose_max = count;
+ loose_data->loose_array = new_arr;
+}
+
+static void lineart_join_loose_edge_arr(LooseEdgeData *loose_data, LooseEdgeData *to_be_joined)
+{
+ if (!to_be_joined->loose_array) {
+ return;
}
- return edge_flag_result;
+ int new_count = loose_data->loose_count + to_be_joined->loose_count;
+ if (new_count >= loose_data->loose_max) {
+ lineart_loose_data_reallocate(loose_data, new_count);
+ }
+ memcpy(&loose_data->loose_array[loose_data->loose_count],
+ to_be_joined->loose_array,
+ sizeof(MEdge *) * to_be_joined->loose_count);
+ loose_data->loose_count += to_be_joined->loose_count;
+ MEM_freeN(to_be_joined->loose_array);
+}
+
+static void lineart_add_loose_edge(LooseEdgeData *loose_data, MEdge *e)
+{
+ if (loose_data->loose_count >= loose_data->loose_max) {
+ int min_amount = MAX2(100, loose_data->loose_count * 2);
+ lineart_loose_data_reallocate(loose_data, min_amount);
+ }
+ loose_data->loose_array[loose_data->loose_count] = e;
+ loose_data->loose_count++;
+}
+
+static void lineart_identify_loose_edges(void *__restrict UNUSED(userdata),
+ const int i,
+ const TaskParallelTLS *__restrict tls)
+{
+ LooseEdgeData *loose_data = (LooseEdgeData *)tls->userdata_chunk;
+ Mesh *me = loose_data->me;
+
+ if (me->medge[i].flag & ME_LOOSEEDGE) {
+ lineart_add_loose_edge(loose_data, &me->medge[i]);
+ }
+}
+
+static void loose_data_sum_reduce(const void *__restrict UNUSED(userdata),
+ void *__restrict chunk_join,
+ void *__restrict chunk)
+{
+ LooseEdgeData *final = (LooseEdgeData *)chunk_join;
+ LooseEdgeData *loose_chunk = (LooseEdgeData *)chunk;
+ lineart_join_loose_edge_arr(final, loose_chunk);
}
static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e)
@@ -1702,298 +1840,421 @@ static void lineart_triangle_adjacent_assign(LineartTriangle *tri,
}
}
-static int lineart_edge_type_duplication_count(char eflag)
+typedef struct TriData {
+ LineartObjectInfo *ob_info;
+ const MLoopTri *mlooptri;
+ LineartVert *vert_arr;
+ LineartTriangle *tri_arr;
+ int lineart_triangle_size;
+ LineartTriangleAdjacent *tri_adj;
+} TriData;
+
+static void lineart_load_tri_task(void *__restrict userdata,
+ const int i,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
- int count = 0;
- /* See eLineartEdgeFlag for details. */
- for (int i = 0; i < 6; i++) {
- if (eflag & (1 << i)) {
- count++;
- }
- }
- return count;
+ TriData *tri_task_data = (TriData *)userdata;
+ Mesh *me = tri_task_data->ob_info->original_me;
+ LineartObjectInfo *ob_info = tri_task_data->ob_info;
+ const MLoopTri *mlooptri = &tri_task_data->mlooptri[i];
+ LineartVert *vert_arr = tri_task_data->vert_arr;
+ LineartTriangle *tri = tri_task_data->tri_arr;
+
+ tri = (LineartTriangle *)(((uchar *)tri) + tri_task_data->lineart_triangle_size * i);
+
+ int v1 = me->mloop[mlooptri->tri[0]].v;
+ int v2 = me->mloop[mlooptri->tri[1]].v;
+ int v3 = me->mloop[mlooptri->tri[2]].v;
+
+ tri->v[0] = &vert_arr[v1];
+ tri->v[1] = &vert_arr[v2];
+ tri->v[2] = &vert_arr[v3];
+
+ /* Material mask bits and occlusion effectiveness assignment. */
+ Material *mat = BKE_object_material_get(ob_info->original_ob,
+ me->mpoly[mlooptri->poly].mat_nr + 1);
+ tri->material_mask_bits |= ((mat && (mat->lineart.flags & LRT_MATERIAL_MASK_ENABLED)) ?
+ mat->lineart.material_mask_bits :
+ 0);
+ tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1);
+ tri->flags |= (mat && (mat->blend_flag & MA_BL_CULL_BACKFACE)) ?
+ LRT_TRIANGLE_MAT_BACK_FACE_CULLING :
+ 0;
+
+ tri->intersection_mask = ob_info->override_intersection_mask;
+
+ double gn[3];
+ float no[3];
+ normal_tri_v3(no, me->mvert[v1].co, me->mvert[v2].co, me->mvert[v3].co);
+ copy_v3db_v3fl(gn, no);
+ mul_v3_mat3_m4v3_db(tri->gn, ob_info->normal, gn);
+ normalize_v3_db(tri->gn);
+
+ if (ob_info->usage == OBJECT_LRT_INTERSECTION_ONLY) {
+ tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY;
+ }
+ else if (ob_info->usage == OBJECT_LRT_NO_INTERSECTION ||
+ ob_info->usage == OBJECT_LRT_OCCLUSION_ONLY) {
+ tri->flags |= LRT_TRIANGLE_NO_INTERSECTION;
+ }
+
+ /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */
+ tri->intersecting_verts = (void *)&tri_task_data->tri_adj[i];
}
-static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBuffer *rb)
+typedef struct EdgeNeighborData {
+ LineartEdgeNeighbor *edge_nabr;
+ LineartAdjacentEdge *adj_e;
+ MLoopTri *mlooptri;
+ MLoop *mloop;
+} EdgeNeighborData;
+
+static void lineart_edge_neighbor_init_task(void *__restrict userdata,
+ const int i,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
- BMesh *bm;
- BMVert *v;
- BMFace *f;
- BMEdge *e;
- BMLoop *loop;
- LineartEdge *la_e;
- LineartEdgeSegment *la_s;
- LineartTriangle *tri;
- LineartTriangleAdjacent *orta;
- double(*model_view_proj)[4] = obi->model_view_proj, (*model_view)[4] = obi->model_view,
- (*normal)[4] = obi->normal;
- LineartElementLinkNode *eln;
- LineartVert *orv;
- LineartEdge *o_la_e;
- LineartEdgeSegment *o_la_s;
- LineartTriangle *ort;
- Object *orig_ob;
- bool can_find_freestyle_edge = false;
- bool can_find_freestyle_face = false;
- int i;
- float use_crease = 0;
+ EdgeNeighborData *en_data = (EdgeNeighborData *)userdata;
+ LineartAdjacentEdge *adj_e = &en_data->adj_e[i];
+ MLoopTri *looptri = &en_data->mlooptri[i / 3];
+ LineartEdgeNeighbor *edge_nabr = &en_data->edge_nabr[i];
+ MLoop *mloop = en_data->mloop;
+
+ adj_e->e = i;
+ adj_e->v1 = mloop[looptri->tri[i % 3]].v;
+ adj_e->v2 = mloop[looptri->tri[(i + 1) % 3]].v;
+ if (adj_e->v1 > adj_e->v2) {
+ SWAP(unsigned int, adj_e->v1, adj_e->v2);
+ }
+ edge_nabr->e = -1;
+
+ edge_nabr->v1 = adj_e->v1;
+ edge_nabr->v2 = adj_e->v2;
+ edge_nabr->flags = 0;
+}
- int usage = obi->usage;
+static LineartEdgeNeighbor *lineart_build_edge_neighbor(Mesh *me, int total_edges)
+{
+ /* Because the mesh is triangulated, so `me->totedge` should be reliable? */
+ LineartAdjacentEdge *adj_e = MEM_mallocN(sizeof(LineartAdjacentEdge) * total_edges,
+ "LineartAdjacentEdge arr");
+ LineartEdgeNeighbor *edge_nabr = MEM_mallocN(sizeof(LineartEdgeNeighbor) * total_edges,
+ "LineartEdgeNeighbor arr");
- if (obi->original_me->edit_mesh) {
- /* Do not use edit_mesh directly because we will modify it, so create a copy. */
- bm = BM_mesh_copy(obi->original_me->edit_mesh->bm);
- }
- else {
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(((Mesh *)(obi->original_me)));
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
- BM_mesh_bm_from_me(bm,
- obi->original_me,
- &((struct BMeshFromMeshParams){
- .calc_face_normal = true,
- .calc_vert_normal = true,
- }));
- }
+ MLoopTri *mlooptri = me->runtime.looptris.array;
- if (obi->free_use_mesh) {
- BKE_id_free(NULL, obi->original_me);
- }
+ TaskParallelSettings en_settings;
+ BLI_parallel_range_settings_defaults(&en_settings);
+ /* Set the minimum amount of edges a thread has to process. */
+ en_settings.min_iter_per_thread = 50000;
+
+ EdgeNeighborData en_data;
+ en_data.adj_e = adj_e;
+ en_data.edge_nabr = edge_nabr;
+ en_data.mlooptri = mlooptri;
+ en_data.mloop = me->mloop;
+
+ BLI_task_parallel_range(0, total_edges, &en_data, lineart_edge_neighbor_init_task, &en_settings);
+
+ lineart_sort_adjacent_items(adj_e, total_edges);
- if (rb->remove_doubles) {
- BMEditMesh *em = BKE_editmesh_create(bm);
- BMOperator findop, weldop;
+ for (int i = 0; i < total_edges - 1; i++) {
+ if (adj_e[i].v1 == adj_e[i + 1].v1 && adj_e[i].v2 == adj_e[i + 1].v2) {
+ edge_nabr[adj_e[i].e].e = adj_e[i + 1].e;
+ edge_nabr[adj_e[i + 1].e].e = adj_e[i].e;
+ }
+ }
- /* See bmesh_opdefines.c and bmesh_operators.c for op names and argument formatting. */
- BMO_op_initf(bm, &findop, BMO_FLAG_DEFAULTS, "find_doubles verts=%av dist=%f", 0.0001);
+ MEM_freeN(adj_e);
- BMO_op_exec(bm, &findop);
+ return edge_nabr;
+}
- /* Weld the vertices. */
- BMO_op_init(bm, &weldop, BMO_FLAG_DEFAULTS, "weld_verts");
- BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap");
- BMO_op_exec(bm, &weldop);
+static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRenderBuffer *re_buf)
+{
+ LineartElementLinkNode *elem_link_node;
+ LineartVert *la_v_arr;
+ LineartEdge *la_edge_arr;
+ LineartEdgeSegment *la_seg_arr;
+ LineartTriangle *la_tri_arr;
- BMO_op_finish(bm, &findop);
- BMO_op_finish(bm, &weldop);
+ Mesh *me = ob_info->original_me;
- MEM_freeN(em);
+ if (!me->totedge) {
+ return;
}
- BM_mesh_elem_hflag_disable_all(bm, BM_FACE | BM_EDGE, BM_ELEM_TAG, false);
- BM_mesh_triangulate(
- bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_BEAUTY, 4, false, NULL, NULL, NULL);
- BM_mesh_normals_update(bm);
- BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
- BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
+ /* Triangulate. */
+ const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(me);
+ const int tot_tri = BKE_mesh_runtime_looptri_len(me);
- if (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) {
- can_find_freestyle_edge = 1;
+ /* Check if we should look for custom data tags like Freestyle edges or faces. */
+ bool can_find_freestyle_edge = false;
+ int layer_index = CustomData_get_active_layer_index(&me->edata, CD_FREESTYLE_EDGE);
+ if (layer_index != -1) {
+ can_find_freestyle_edge = true;
}
- if (CustomData_has_layer(&bm->pdata, CD_FREESTYLE_FACE)) {
+
+ bool can_find_freestyle_face = false;
+ layer_index = CustomData_get_active_layer_index(&me->pdata, CD_FREESTYLE_FACE);
+ if (layer_index != -1) {
can_find_freestyle_face = true;
}
/* If we allow duplicated edges, one edge should get added multiple times if is has been
* classified as more than one edge type. This is so we can create multiple different line type
* chains containing the same edge. */
- orv = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartVert) * bm->totvert);
- ort = lineart_mem_acquire_thread(&rb->render_data_pool, bm->totface * rb->triangle_size);
+ la_v_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool,
+ sizeof(LineartVert) * me->totvert);
+ la_tri_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool,
+ tot_tri * re_buf->triangle_size);
- orig_ob = obi->original_ob;
+ Object *orig_ob = ob_info->original_ob;
- BLI_spin_lock(&rb->lock_task);
- eln = lineart_list_append_pointer_pool_sized_thread(
- &rb->vertex_buffer_pointers, &rb->render_data_pool, orv, sizeof(LineartElementLinkNode));
- BLI_spin_unlock(&rb->lock_task);
+ BLI_spin_lock(&re_buf->lock_task);
+ elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->vertex_buffer_pointers,
+ &re_buf->render_data_pool,
+ la_v_arr,
+ sizeof(LineartElementLinkNode));
+ BLI_spin_unlock(&re_buf->lock_task);
- eln->element_count = bm->totvert;
- eln->object_ref = orig_ob;
- obi->v_eln = eln;
+ elem_link_node->element_count = me->totvert;
+ elem_link_node->object_ref = orig_ob;
+ ob_info->v_eln = elem_link_node;
bool use_auto_smooth = false;
+ float crease_angle = 0;
if (orig_ob->lineart.flags & OBJECT_LRT_OWN_CREASE) {
- use_crease = cosf(M_PI - orig_ob->lineart.crease_threshold);
+ crease_angle = cosf(M_PI - orig_ob->lineart.crease_threshold);
}
- else if (obi->original_me->flag & ME_AUTOSMOOTH) {
- use_crease = cosf(obi->original_me->smoothresh);
+ else if (ob_info->original_me->flag & ME_AUTOSMOOTH) {
+ crease_angle = cosf(ob_info->original_me->smoothresh);
use_auto_smooth = true;
}
else {
- use_crease = rb->crease_threshold;
+ crease_angle = re_buf->crease_threshold;
}
/* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates
* erroneous detection on creases. Future configuration should allow options. */
if (orig_ob->type == OB_FONT) {
- eln->flags |= LRT_ELEMENT_BORDER_ONLY;
+ elem_link_node->flags |= LRT_ELEMENT_BORDER_ONLY;
}
- BLI_spin_lock(&rb->lock_task);
- eln = lineart_list_append_pointer_pool_sized_thread(
- &rb->triangle_buffer_pointers, &rb->render_data_pool, ort, sizeof(LineartElementLinkNode));
- BLI_spin_unlock(&rb->lock_task);
+ BLI_spin_lock(&re_buf->lock_task);
+ elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->triangle_buffer_pointers,
+ &re_buf->render_data_pool,
+ la_tri_arr,
+ sizeof(LineartElementLinkNode));
+ BLI_spin_unlock(&re_buf->lock_task);
- eln->element_count = bm->totface;
- eln->object_ref = orig_ob;
- eln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0);
+ int usage = ob_info->usage;
+
+ elem_link_node->element_count = tot_tri;
+ elem_link_node->object_ref = orig_ob;
+ elem_link_node->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0);
/* Note this memory is not from pool, will be deleted after culling. */
- orta = MEM_callocN(sizeof(LineartTriangleAdjacent) * bm->totface, "LineartTriangleAdjacent");
+ LineartTriangleAdjacent *tri_adj = MEM_callocN(sizeof(LineartTriangleAdjacent) * tot_tri,
+ "LineartTriangleAdjacent");
/* Link is minimal so we use pool anyway. */
- BLI_spin_lock(&rb->lock_task);
+ BLI_spin_lock(&re_buf->lock_task);
lineart_list_append_pointer_pool_thread(
- &rb->triangle_adjacent_pointers, &rb->render_data_pool, orta);
- BLI_spin_unlock(&rb->lock_task);
-
- for (i = 0; i < bm->totvert; i++) {
- v = BM_vert_at_index(bm, i);
- lineart_vert_transform(v, i, orv, model_view, model_view_proj);
- orv[i].index = i;
- }
+ &re_buf->triangle_adjacent_pointers, &re_buf->render_data_pool, tri_adj);
+ BLI_spin_unlock(&re_buf->lock_task);
+
+ /* Convert all vertices to lineart verts. */
+ TaskParallelSettings vert_settings;
+ BLI_parallel_range_settings_defaults(&vert_settings);
+ /* Set the minimum amount of verts a thread has to process. */
+ vert_settings.min_iter_per_thread = 4000;
+
+ VertData vert_data;
+ vert_data.mvert = me->mvert;
+ vert_data.v_arr = la_v_arr;
+ vert_data.model_view = ob_info->model_view;
+ vert_data.model_view_proj = ob_info->model_view_proj;
+
+ BLI_task_parallel_range(
+ 0, me->totvert, &vert_data, lineart_mvert_transform_task, &vert_settings);
/* Register a global index increment. See #lineart_triangle_share_edge() and
* #lineart_main_load_geometries() for detailed. It's okay that global_vindex might eventually
* overflow, in such large scene it's virtually impossible for two vertex of the same numeric
* index to come close together. */
- obi->global_i_offset = bm->totvert;
-
- tri = ort;
- for (i = 0; i < bm->totface; i++) {
- f = BM_face_at_index(bm, i);
-
- loop = f->l_first;
- tri->v[0] = &orv[BM_elem_index_get(loop->v)];
- loop = loop->next;
- tri->v[1] = &orv[BM_elem_index_get(loop->v)];
- loop = loop->next;
- tri->v[2] = &orv[BM_elem_index_get(loop->v)];
-
- /* Material mask bits and occlusion effectiveness assignment. */
- Material *mat = BKE_object_material_get(orig_ob, f->mat_nr + 1);
- tri->material_mask_bits |= ((mat && (mat->lineart.flags & LRT_MATERIAL_MASK_ENABLED)) ?
- mat->lineart.material_mask_bits :
- 0);
- tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1);
- tri->flags |= (mat && (mat->blend_flag & MA_BL_CULL_BACKFACE)) ?
- LRT_TRIANGLE_MAT_BACK_FACE_CULLING :
- 0;
-
- tri->intersection_mask = obi->override_intersection_mask;
-
- double gn[3];
- copy_v3db_v3fl(gn, f->no);
- mul_v3_mat3_m4v3_db(tri->gn, normal, gn);
- normalize_v3_db(tri->gn);
-
- if (usage == OBJECT_LRT_INTERSECTION_ONLY) {
- tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY;
- }
- else if (ELEM(usage, OBJECT_LRT_NO_INTERSECTION, OBJECT_LRT_OCCLUSION_ONLY)) {
- tri->flags |= LRT_TRIANGLE_NO_INTERSECTION;
- }
-
- /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */
- tri->intersecting_verts = (void *)&orta[i];
-
- tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size);
- }
-
- /* Use BM_ELEM_TAG in f->head.hflag to store needed faces in the first iteration. */
-
- int allocate_la_e = 0;
- for (i = 0; i < bm->totedge; i++) {
- e = BM_edge_at_index(bm, i);
-
- /* Because e->head.hflag is char, so line type flags should not exceed positive 7 bits. */
- uint16_t eflag = lineart_identify_feature_line(rb,
- e,
- ort,
- orv,
- use_crease,
- use_auto_smooth,
- can_find_freestyle_edge,
- can_find_freestyle_face,
- bm);
- if (eflag) {
- /* Only allocate for feature lines (instead of all lines) to save memory.
- * If allow duplicated edges, one edge gets added multiple times if it has multiple types.
- */
- allocate_la_e += rb->allow_duplicated_types ? lineart_edge_type_duplication_count(eflag) : 1;
+ ob_info->global_i_offset = me->totvert;
+
+ /* Convert all mesh triangles into lineart triangles.
+ * Also create an edge map to get connectivity between edges and triangles. */
+ TaskParallelSettings tri_settings;
+ BLI_parallel_range_settings_defaults(&tri_settings);
+ /* Set the minimum amount of triangles a thread has to process. */
+ tri_settings.min_iter_per_thread = 4000;
+
+ TriData tri_data;
+ tri_data.ob_info = ob_info;
+ tri_data.mlooptri = mlooptri;
+ tri_data.vert_arr = la_v_arr;
+ tri_data.tri_arr = la_tri_arr;
+ tri_data.lineart_triangle_size = re_buf->triangle_size;
+ tri_data.tri_adj = tri_adj;
+
+ unsigned int total_edges = tot_tri * 3;
+
+ BLI_task_parallel_range(0, tot_tri, &tri_data, lineart_load_tri_task, &tri_settings);
+
+ /* Check for contour lines in the mesh.
+ * IE check if the triangle edges lies in area where the triangles go from front facing to back
+ * facing.
+ */
+ EdgeFeatReduceData edge_reduce = {0};
+ TaskParallelSettings edge_feat_settings;
+ BLI_parallel_range_settings_defaults(&edge_feat_settings);
+ /* Set the minimum amount of edges a thread has to process. */
+ edge_feat_settings.min_iter_per_thread = 4000;
+ edge_feat_settings.userdata_chunk = &edge_reduce;
+ edge_feat_settings.userdata_chunk_size = sizeof(EdgeFeatReduceData);
+ edge_feat_settings.func_reduce = feat_data_sum_reduce;
+
+ EdgeFeatData edge_feat_data = {0};
+ edge_feat_data.rb = re_buf;
+ edge_feat_data.me = me;
+ edge_feat_data.mlooptri = mlooptri;
+ edge_feat_data.edge_nabr = lineart_build_edge_neighbor(me, total_edges);
+ edge_feat_data.tri_array = la_tri_arr;
+ edge_feat_data.v_array = la_v_arr;
+ edge_feat_data.crease_threshold = crease_angle;
+ edge_feat_data.use_auto_smooth = use_auto_smooth;
+ edge_feat_data.use_freestyle_face = can_find_freestyle_face;
+ edge_feat_data.use_freestyle_edge = can_find_freestyle_edge;
+ if (edge_feat_data.use_freestyle_face) {
+ edge_feat_data.freestyle_face_index = CustomData_get_layer_index(&me->pdata,
+ CD_FREESTYLE_FACE);
+ }
+ if (edge_feat_data.use_freestyle_edge) {
+ edge_feat_data.freestyle_edge_index = CustomData_get_layer_index(&me->edata,
+ CD_FREESTYLE_EDGE);
+ }
+
+ BLI_task_parallel_range(0,
+ total_edges,
+ &edge_feat_data,
+ lineart_identify_mlooptri_feature_edges,
+ &edge_feat_settings);
+
+ LooseEdgeData loose_data = {0};
+ if (re_buf->use_loose) {
+ /* Only identifying floating edges at this point because other edges has been taken care of
+ * inside #lineart_identify_mlooptri_feature_edges function. */
+ TaskParallelSettings edge_loose_settings;
+ BLI_parallel_range_settings_defaults(&edge_loose_settings);
+ edge_loose_settings.min_iter_per_thread = 4000;
+ edge_loose_settings.func_reduce = loose_data_sum_reduce;
+ edge_loose_settings.userdata_chunk = &loose_data;
+ edge_loose_settings.userdata_chunk_size = sizeof(LooseEdgeData);
+ loose_data.me = me;
+ BLI_task_parallel_range(
+ 0, me->totedge, &loose_data, lineart_identify_loose_edges, &edge_loose_settings);
+ }
+
+ int allocate_la_e = edge_reduce.feat_edges + loose_data.loose_count;
+
+ la_edge_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool,
+ sizeof(LineartEdge) * allocate_la_e);
+ la_seg_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool,
+ sizeof(LineartEdgeSegment) * allocate_la_e);
+ BLI_spin_lock(&re_buf->lock_task);
+ elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->line_buffer_pointers,
+ &re_buf->render_data_pool,
+ la_edge_arr,
+ sizeof(LineartElementLinkNode));
+ BLI_spin_unlock(&re_buf->lock_task);
+ elem_link_node->element_count = allocate_la_e;
+ elem_link_node->object_ref = orig_ob;
+
+ // Start of the edge/seg arr
+ LineartEdge *la_edge;
+ LineartEdgeSegment *la_seg;
+ la_edge = la_edge_arr;
+ la_seg = la_seg_arr;
+
+ for (int i = 0; i < total_edges; i++) {
+ LineartEdgeNeighbor *edge_nabr = &edge_feat_data.edge_nabr[i];
+
+ if (i < edge_nabr->e) {
+ continue;
}
- /* Here we just use bm's flag for when loading actual lines, then we don't need to call
- * lineart_identify_feature_line() again, e->head.hflag deleted after loading anyway. Always
- * set the flag, so hflag stays 0 for lines that are not feature lines. */
- e->head.hflag = eflag;
- }
-
- o_la_e = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e);
- o_la_s = lineart_mem_acquire_thread(&rb->render_data_pool,
- sizeof(LineartEdgeSegment) * allocate_la_e);
- BLI_spin_lock(&rb->lock_task);
- eln = lineart_list_append_pointer_pool_sized_thread(
- &rb->line_buffer_pointers, &rb->render_data_pool, o_la_e, sizeof(LineartElementLinkNode));
- BLI_spin_unlock(&rb->lock_task);
- eln->element_count = allocate_la_e;
- eln->object_ref = orig_ob;
-
- la_e = o_la_e;
- la_s = o_la_s;
- for (i = 0; i < bm->totedge; i++) {
- e = BM_edge_at_index(bm, i);
/* Not a feature line, so we skip. */
- if (!e->head.hflag) {
+ if (edge_nabr->flags == 0) {
continue;
}
bool edge_added = false;
/* See eLineartEdgeFlag for details. */
- for (int flag_bit = 0; flag_bit < 6; flag_bit++) {
+ for (int flag_bit = 0; flag_bit < LRT_EDGE_FLAG_TYPE_MAX_BITS; flag_bit++) {
char use_type = 1 << flag_bit;
- if (!(use_type & e->head.hflag)) {
+ if (!(use_type & edge_nabr->flags)) {
continue;
}
- la_e->v1 = &orv[BM_elem_index_get(e->v1)];
- la_e->v2 = &orv[BM_elem_index_get(e->v2)];
- la_e->v1_obindex = la_e->v1->index;
- la_e->v2_obindex = la_e->v2->index;
- if (e->l) {
- int findex = BM_elem_index_get(e->l->f);
- la_e->t1 = lineart_triangle_from_index(rb, ort, findex);
+ la_edge->v1 = &la_v_arr[edge_nabr->v1];
+ la_edge->v2 = &la_v_arr[edge_nabr->v2];
+ la_edge->v1_obindex = la_edge->v1->index;
+ la_edge->v2_obindex = la_edge->v2->index;
+ int findex = i / 3;
+ la_edge->t1 = lineart_triangle_from_index(re_buf, la_tri_arr, findex);
+ if (!edge_added) {
+ lineart_triangle_adjacent_assign(la_edge->t1, &tri_adj[findex], la_edge);
+ }
+ if (edge_nabr->e != -1) {
+ findex = edge_nabr->e / 3;
+ la_edge->t2 = lineart_triangle_from_index(re_buf, la_tri_arr, findex);
if (!edge_added) {
- lineart_triangle_adjacent_assign(la_e->t1, &orta[findex], la_e);
- }
- if (e->l->radial_next && e->l->radial_next != e->l) {
- findex = BM_elem_index_get(e->l->radial_next->f);
- la_e->t2 = lineart_triangle_from_index(rb, ort, findex);
- if (!edge_added) {
- lineart_triangle_adjacent_assign(la_e->t2, &orta[findex], la_e);
- }
+ lineart_triangle_adjacent_assign(la_edge->t2, &tri_adj[findex], la_edge);
}
}
- la_e->flags = use_type;
- la_e->object_ref = orig_ob;
- BLI_addtail(&la_e->segments, la_s);
- if (ELEM(usage, OBJECT_LRT_INHERIT, OBJECT_LRT_INCLUDE, OBJECT_LRT_NO_INTERSECTION)) {
- lineart_add_edge_to_list_thread(obi, la_e);
+ la_edge->flags = use_type;
+ la_edge->object_ref = orig_ob;
+ BLI_addtail(&la_edge->segments, la_seg);
+ if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE ||
+ usage == OBJECT_LRT_NO_INTERSECTION) {
+ lineart_add_edge_to_list_thread(ob_info, la_edge);
}
edge_added = true;
- la_e++;
- la_s++;
+ la_edge++;
+ la_seg++;
- if (!rb->allow_duplicated_types) {
+ if (!re_buf->allow_duplicated_types) {
break;
}
}
}
- /* always free bm as it's a copy from before threading */
- BM_mesh_free(bm);
+ if (loose_data.loose_array) {
+ for (int i = 0; i < loose_data.loose_count; i++) {
+ la_edge->v1 = &la_v_arr[loose_data.loose_array[i]->v1];
+ la_edge->v2 = &la_v_arr[loose_data.loose_array[i]->v2];
+ la_edge->v1_obindex = la_edge->v1->index;
+ la_edge->v2_obindex = la_edge->v2->index;
+ la_edge->flags = LRT_EDGE_FLAG_LOOSE;
+ la_edge->object_ref = orig_ob;
+ BLI_addtail(&la_edge->segments, la_seg);
+ if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE ||
+ usage == OBJECT_LRT_NO_INTERSECTION) {
+ lineart_add_edge_to_list_thread(ob_info, la_edge);
+ }
+ la_edge++;
+ la_seg++;
+ }
+ MEM_freeN(loose_data.loose_array);
+ }
+
+ MEM_freeN(edge_feat_data.edge_nabr);
+
+ if (ob_info->free_use_mesh) {
+ BKE_id_free(NULL, me);
+ }
}
static void lineart_object_load_worker(TaskPool *__restrict UNUSED(pool),
@@ -2001,6 +2262,9 @@ static void lineart_object_load_worker(TaskPool *__restrict UNUSED(pool),
{
for (LineartObjectInfo *obi = olti->pending; obi; obi = obi->next) {
lineart_geometry_object_load(obi, olti->rb);
+ if (G.debug_value == 4000) {
+ printf("thread id: %d processed: %d\n", olti->thread_id, obi->original_me->totpoly);
+ }
}
}
@@ -2085,6 +2349,7 @@ static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_
use_olti = &olti_list[i];
}
}
+
use_olti->total_faces += this_face_count;
obi->next = use_olti->pending;
use_olti->pending = obi;
@@ -2093,18 +2358,21 @@ static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_
static bool lineart_geometry_check_visible(double (*model_view_proj)[4],
double shift_x,
double shift_y,
- Object *use_ob)
+ Mesh *use_mesh)
{
- const BoundBox *bb = BKE_object_boundbox_get(use_ob);
- if (!bb) {
- /* For lights and empty stuff there will be no bbox. */
+ if (!use_mesh) {
return false;
}
+ float mesh_min[3], mesh_max[3];
+ INIT_MINMAX(mesh_min, mesh_max);
+ BKE_mesh_minmax(use_mesh, mesh_min, mesh_max);
+ BoundBox bb = {0};
+ BKE_boundbox_init_from_minmax(&bb, mesh_min, mesh_max);
double co[8][4];
double tmp[3];
for (int i = 0; i < 8; i++) {
- copy_v3db_v3fl(co[i], bb->vec[i]);
+ copy_v3db_v3fl(co[i], bb.vec[i]);
copy_v3_v3_db(tmp, co[i]);
mul_v4_m4v3_db(co[i], model_view_proj, tmp);
co[i][0] -= shift_x * 2 * co[i][3];
@@ -2216,20 +2484,13 @@ static void lineart_main_load_geometries(
continue;
}
- if (!lineart_geometry_check_visible(obi->model_view_proj, rb->shift_x, rb->shift_y, use_ob)) {
- if (G.debug_value == 4000) {
- bound_box_discard_count++;
- }
- continue;
- }
-
if (use_ob->type == OB_MESH) {
use_mesh = BKE_object_get_evaluated_mesh(use_ob);
}
else {
/* If DEG_ITER_OBJECT_FLAG_DUPLI is set, some curve objects may also have an evaluated mesh
- * object in the list. To avoid adding duplicate geometry, ignore evaluated curve objects in
- * those cases. */
+ * object in the list. To avoid adding duplicate geometry, ignore evaluated curve objects
+ * in those cases. */
if (allow_duplicates && BKE_object_get_evaluated_mesh(ob) != NULL) {
continue;
}
@@ -2241,6 +2502,17 @@ static void lineart_main_load_geometries(
continue;
}
+ if (!lineart_geometry_check_visible(
+ obi->model_view_proj, rb->shift_x, rb->shift_y, use_mesh)) {
+ if (ob->type != OB_MESH) {
+ BKE_id_free(NULL, use_mesh);
+ }
+ if (G.debug_value == 4000) {
+ bound_box_discard_count++;
+ }
+ continue;
+ }
+
if (ob->type != OB_MESH) {
obi->free_use_mesh = true;
}
@@ -2259,9 +2531,12 @@ static void lineart_main_load_geometries(
TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH);
+ if (G.debug_value == 4000) {
+ printf("thread count: %d\n", thread_count);
+ }
for (int i = 0; i < thread_count; i++) {
olti[i].rb = rb;
- olti[i].dg = depsgraph;
+ olti[i].thread_id = i;
BLI_task_pool_push(tp, (TaskRunFunction)lineart_object_load_worker, &olti[i], 0, NULL);
}
BLI_task_pool_work_and_wait(tp);
@@ -3003,7 +3278,7 @@ static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb,
double *G0 = tri->v[0]->gloc, *G1 = tri->v[1]->gloc, *G2 = tri->v[2]->gloc;
- /* If this is not the smallest subdivision bounding area. */
+ /* If this is not the smallest subdiv bounding area. */
if (ba->child) {
lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[0]);
lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[1]);
@@ -3187,7 +3462,6 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene,
rb->fuzzy_intersections = (lmd->calculation_flags & LRT_INTERSECTION_AS_CONTOUR) != 0;
rb->fuzzy_everything = (lmd->calculation_flags & LRT_EVERYTHING_AS_CONTOUR) != 0;
rb->allow_boundaries = (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) != 0;
- rb->remove_doubles = (lmd->calculation_flags & LRT_REMOVE_DOUBLES) != 0;
rb->use_loose_as_contour = (lmd->calculation_flags & LRT_LOOSE_AS_CONTOUR) != 0;
rb->use_loose_edge_chain = (lmd->calculation_flags & LRT_CHAIN_LOOSE_EDGES) != 0;
rb->use_geometry_space_chain = (lmd->calculation_flags & LRT_CHAIN_GEOMETRY_SPACE) != 0;
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
index 508061d5d98..b1aa81322a8 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
@@ -121,3 +121,13 @@ void lineart_count_and_print_render_buffer_memory(struct LineartRenderBuffer *rb
/* Initial bounding area row/column count, setting 4 is the simplest way algorithm could function
* efficiently. */
#define LRT_BA_ROWS 4
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void lineart_sort_adjacent_items(LineartAdjacentEdge *ai, int length);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 7c6390971dd..eac33f6c705 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -303,6 +303,14 @@ set(GLSL_SRC
shaders/gpu_shader_geometry.glsl
+ shaders/common/gpu_shader_common_color_ramp.glsl
+ shaders/common/gpu_shader_common_color_utils.glsl
+ shaders/common/gpu_shader_common_curves.glsl
+ shaders/common/gpu_shader_common_hash.glsl
+ shaders/common/gpu_shader_common_math.glsl
+ shaders/common/gpu_shader_common_math_utils.glsl
+ shaders/common/gpu_shader_common_mix_rgb.glsl
+
shaders/material/gpu_shader_material_add_shader.glsl
shaders/material/gpu_shader_material_ambient_occlusion.glsl
shaders/material/gpu_shader_material_anisotropic.glsl
@@ -315,8 +323,7 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_bump.glsl
shaders/material/gpu_shader_material_camera.glsl
shaders/material/gpu_shader_material_clamp.glsl
- shaders/material/gpu_shader_material_color_ramp.glsl
- shaders/material/gpu_shader_material_color_util.glsl
+ shaders/material/gpu_shader_material_combine_color.glsl
shaders/material/gpu_shader_material_combine_hsv.glsl
shaders/material/gpu_shader_material_combine_rgb.glsl
shaders/material/gpu_shader_material_combine_xyz.glsl
@@ -324,7 +331,6 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_displacement.glsl
shaders/material/gpu_shader_material_eevee_specular.glsl
shaders/material/gpu_shader_material_emission.glsl
- shaders/material/gpu_shader_material_float_curve.glsl
shaders/material/gpu_shader_material_fractal_noise.glsl
shaders/material/gpu_shader_material_fresnel.glsl
shaders/material/gpu_shader_material_gamma.glsl
@@ -333,7 +339,6 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_glossy.glsl
shaders/material/gpu_shader_material_hair_info.glsl
shaders/material/gpu_shader_material_hair.glsl
- shaders/material/gpu_shader_material_hash.glsl
shaders/material/gpu_shader_material_holdout.glsl
shaders/material/gpu_shader_material_hue_sat_val.glsl
shaders/material/gpu_shader_material_invert.glsl
@@ -342,9 +347,6 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_light_path.glsl
shaders/material/gpu_shader_material_mapping.glsl
shaders/material/gpu_shader_material_map_range.glsl
- shaders/material/gpu_shader_material_math.glsl
- shaders/material/gpu_shader_material_math_util.glsl
- shaders/material/gpu_shader_material_mix_rgb.glsl
shaders/material/gpu_shader_material_mix_shader.glsl
shaders/material/gpu_shader_material_noise.glsl
shaders/material/gpu_shader_material_normal.glsl
@@ -357,8 +359,8 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_point_info.glsl
shaders/material/gpu_shader_material_principled.glsl
shaders/material/gpu_shader_material_refraction.glsl
- shaders/material/gpu_shader_material_rgb_curves.glsl
shaders/material/gpu_shader_material_rgb_to_bw.glsl
+ shaders/material/gpu_shader_material_separate_color.glsl
shaders/material/gpu_shader_material_separate_hsv.glsl
shaders/material/gpu_shader_material_separate_rgb.glsl
shaders/material/gpu_shader_material_separate_xyz.glsl
@@ -381,10 +383,10 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_tex_wave.glsl
shaders/material/gpu_shader_material_tex_white_noise.glsl
shaders/material/gpu_shader_material_toon.glsl
+ shaders/material/gpu_shader_material_transform_utils.glsl
shaders/material/gpu_shader_material_translucent.glsl
shaders/material/gpu_shader_material_transparent.glsl
shaders/material/gpu_shader_material_uv_map.glsl
- shaders/material/gpu_shader_material_vector_curves.glsl
shaders/material/gpu_shader_material_vector_displacement.glsl
shaders/material/gpu_shader_material_vector_math.glsl
shaders/material/gpu_shader_material_vector_rotate.glsl
@@ -496,6 +498,7 @@ set(SRC_SHADER_CREATE_INFOS
shaders/infos/gpu_shader_2D_widget_info.hh
shaders/infos/gpu_shader_3D_depth_only_info.hh
shaders/infos/gpu_shader_3D_flat_color_info.hh
+ shaders/infos/gpu_shader_3D_image_info.hh
shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh
shaders/infos/gpu_shader_3D_point_info.hh
shaders/infos/gpu_shader_3D_polyline_info.hh
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index b620fe9cc9d..3460d33fe68 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -280,6 +280,16 @@ typedef enum eGPUBuiltinShader {
GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE,
GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR,
/**
+ * Draw a texture in 3D. Take a 3D position and a 2D texture coordinate for each vertex.
+ *
+ * Exposed via Python-API for add-ons.
+ *
+ * \param image: uniform sampler2D
+ * \param texCoord: in vec2
+ * \param pos: in vec3
+ */
+ GPU_SHADER_3D_IMAGE,
+ /**
* Draw texture with alpha. Take a 3D position and a 2D texture coordinate for each vertex.
*
* \param alpha: uniform float
diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h
index bb0912f284b..b045d908438 100644
--- a/source/blender/gpu/GPU_texture.h
+++ b/source/blender/gpu/GPU_texture.h
@@ -280,9 +280,9 @@ void *GPU_texture_read(GPUTexture *tex, eGPUDataFormat data_format, int miplvl);
/**
* Fills the whole texture with the same data for all pixels.
* \warning Only work for 2D texture for now.
- * \warning Only clears the mip 0 of the texture.
+ * \warning Only clears the MIP 0 of the texture.
* \param data_format: data format of the pixel data.
- * \note The format is float for unorm textures.
+ * \note The format is float for UNORM textures.
* \param data: 1 pixel worth of data to fill the texture with.
*/
void GPU_texture_clear(GPUTexture *tex, eGPUDataFormat data_format, const void *data);
diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h
index c6b0d5902fe..bf8dc409cb7 100644
--- a/source/blender/gpu/GPU_vertex_format.h
+++ b/source/blender/gpu/GPU_vertex_format.h
@@ -55,7 +55,7 @@ typedef struct GPUVertAttr {
/* 1 to 4 or 8 or 12 or 16 */
uint comp_len : 5;
/* size in bytes, 1 to 64 */
- uint sz : 7;
+ uint size : 7;
/* from beginning of vertex, in bytes */
uint offset : 11;
/* up to GPU_VERT_ATTR_MAX_NAMES */
diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc
index 6ef015492c7..4ec215c2d3b 100644
--- a/source/blender/gpu/intern/gpu_capabilities.cc
+++ b/source/blender/gpu/intern/gpu_capabilities.cc
@@ -172,7 +172,6 @@ int GPU_max_compute_shader_storage_blocks()
return GCaps.max_compute_shader_storage_blocks;
}
-
/** \} */
/* -------------------------------------------------------------------- */
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_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c
index 13238a03688..b92fae4a89b 100644
--- a/source/blender/gpu/intern/gpu_shader_builtin.c
+++ b/source/blender/gpu/intern/gpu_shader_builtin.c
@@ -150,6 +150,11 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = {
.name = "GPU_SHADER_SIMPLE_LIGHTING",
.create_info = "gpu_shader_simple_lighting",
},
+ [GPU_SHADER_3D_IMAGE] =
+ {
+ .name = "GPU_SHADER_3D_IMAGE",
+ .create_info = "gpu_shader_3D_image",
+ },
[GPU_SHADER_3D_IMAGE_MODULATE_ALPHA] =
{
.name = "GPU_SHADER_3D_IMAGE_MODULATE_ALPHA",
@@ -367,6 +372,16 @@ GPUShader *GPU_shader_get_builtin_shader_with_config(eGPUBuiltinShader shader,
if (sh_cfg == GPU_SHADER_CFG_DEFAULT) {
if (stages->create_info != NULL) {
*sh_p = GPU_shader_create_from_info_name(stages->create_info);
+ if (ELEM(shader,
+ GPU_SHADER_3D_POLYLINE_CLIPPED_UNIFORM_COLOR,
+ GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR,
+ GPU_SHADER_3D_POLYLINE_FLAT_COLOR,
+ GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR)) {
+ /* Set a default value for `lineSmooth`.
+ * Ideally this value should be set by the caller. */
+ GPU_shader_bind(*sh_p);
+ GPU_shader_uniform_1i(*sh_p, "lineSmooth", 1);
+ }
}
else {
*sh_p = GPU_shader_create_from_arrays_named(
diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc
index 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..13f409cfba5 100644
--- a/source/blender/gpu/intern/gpu_vertex_buffer.cc
+++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc
@@ -199,7 +199,7 @@ void GPU_vertbuf_attr_set(GPUVertBuf *verts_, uint a_idx, uint v_idx, const void
BLI_assert(a_idx < format->attr_len);
BLI_assert(verts->data != nullptr);
verts->flag |= GPU_VERTBUF_DATA_DIRTY;
- memcpy(verts->data + a->offset + v_idx * format->stride, data, a->sz);
+ memcpy(verts->data + a->offset + v_idx * format->stride, data, a->size);
}
void GPU_vertbuf_attr_fill(GPUVertBuf *verts_, uint a_idx, const void *data)
@@ -208,7 +208,7 @@ void GPU_vertbuf_attr_fill(GPUVertBuf *verts_, uint a_idx, const void *data)
const GPUVertFormat *format = &verts->format;
BLI_assert(a_idx < format->attr_len);
const GPUVertAttr *a = &format->attrs[a_idx];
- const uint stride = a->sz; /* tightly packed input data */
+ const uint stride = a->size; /* tightly packed input data */
verts->flag |= GPU_VERTBUF_DATA_DIRTY;
GPU_vertbuf_attr_fill_stride(verts_, a_idx, stride, data);
}
@@ -235,13 +235,13 @@ void GPU_vertbuf_attr_fill_stride(GPUVertBuf *verts_, uint a_idx, uint stride, c
if (format->attr_len == 1 && stride == format->stride) {
/* we can copy it all at once */
- memcpy(verts->data, data, vertex_len * a->sz);
+ memcpy(verts->data, data, vertex_len * a->size);
}
else {
/* we must copy it per vertex */
for (uint v = 0; v < vertex_len; v++) {
memcpy(
- verts->data + a->offset + v * format->stride, (const uchar *)data + v * stride, a->sz);
+ verts->data + a->offset + v * format->stride, (const uchar *)data + v * stride, a->size);
}
}
}
@@ -256,7 +256,7 @@ void GPU_vertbuf_attr_get_raw_data(GPUVertBuf *verts_, uint a_idx, GPUVertBufRaw
verts->flag |= GPU_VERTBUF_DATA_DIRTY;
verts->flag &= ~GPU_VERTBUF_DATA_UPLOADED;
- access->size = a->sz;
+ access->size = a->size;
access->stride = format->stride;
access->data = (uchar *)verts->data + a->offset;
access->data_init = access->data;
diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc
index a9ac191754b..59ae862aa51 100644
--- a/source/blender/gpu/intern/gpu_vertex_format.cc
+++ b/source/blender/gpu/intern/gpu_vertex_format.cc
@@ -49,7 +49,7 @@ void GPU_vertformat_copy(GPUVertFormat *dest, const GPUVertFormat *src)
memcpy(dest, src, sizeof(GPUVertFormat));
}
-static uint comp_sz(GPUVertCompType type)
+static uint comp_size(GPUVertCompType type)
{
#if TRUST_NO_ONE
assert(type <= GPU_COMP_F32); /* other types have irregular sizes (not bytes) */
@@ -58,12 +58,12 @@ static uint comp_sz(GPUVertCompType type)
return sizes[type];
}
-static uint attr_sz(const GPUVertAttr *a)
+static uint attr_size(const GPUVertAttr *a)
{
if (a->comp_type == GPU_COMP_I10) {
return 4; /* always packed as 10_10_10_2 */
}
- return a->comp_len * comp_sz(static_cast<GPUVertCompType>(a->comp_type));
+ return a->comp_len * comp_size(static_cast<GPUVertCompType>(a->comp_type));
}
static uint attr_align(const GPUVertAttr *a)
@@ -71,7 +71,7 @@ static uint attr_align(const GPUVertAttr *a)
if (a->comp_type == GPU_COMP_I10) {
return 4; /* always packed as 10_10_10_2 */
}
- uint c = comp_sz(static_cast<GPUVertCompType>(a->comp_type));
+ uint c = comp_size(static_cast<GPUVertCompType>(a->comp_type));
if (a->comp_len == 3 && c <= 2) {
return 4 * c; /* AMD HW can't fetch these well, so pad it out (other vendors too?) */
}
@@ -156,7 +156,7 @@ uint GPU_vertformat_attr_add(GPUVertFormat *format,
attr->comp_len = (comp_type == GPU_COMP_I10) ?
4 :
comp_len; /* system needs 10_10_10_2 to be 4 or BGRA */
- attr->sz = attr_sz(attr);
+ attr->size = attr_size(attr);
attr->offset = 0; /* offsets & stride are calculated later (during pack) */
attr->fetch_mode = fetch_mode;
@@ -294,13 +294,13 @@ uint padding(uint offset, uint alignment)
}
#if PACK_DEBUG
-static void show_pack(uint a_idx, uint sz, uint pad)
+static void show_pack(uint a_idx, uint size, uint pad)
{
const char c = 'A' + a_idx;
for (uint i = 0; i < pad; i++) {
putchar('-');
}
- for (uint i = 0; i < sz; i++) {
+ for (uint i = 0; i < size; i++) {
putchar(c);
}
}
@@ -310,10 +310,10 @@ void VertexFormat_pack(GPUVertFormat *format)
{
GPUVertAttr *a0 = &format->attrs[0];
a0->offset = 0;
- uint offset = a0->sz;
+ uint offset = a0->size;
#if PACK_DEBUG
- show_pack(0, a0->sz, 0);
+ show_pack(0, a0->size, 0);
#endif
for (uint a_idx = 1; a_idx < format->attr_len; a_idx++) {
@@ -321,10 +321,10 @@ void VertexFormat_pack(GPUVertFormat *format)
uint mid_padding = padding(offset, attr_align(a));
offset += mid_padding;
a->offset = offset;
- offset += a->sz;
+ offset += a->size;
#if PACK_DEBUG
- show_pack(a_idx, a->sz, mid_padding);
+ show_pack(a_idx, a->size, mid_padding);
#endif
}
diff --git a/source/blender/gpu/metal/mtl_texture.hh b/source/blender/gpu/metal/mtl_texture.hh
index e013bb5321a..47f03a5777b 100644
--- a/source/blender/gpu/metal/mtl_texture.hh
+++ b/source/blender/gpu/metal/mtl_texture.hh
@@ -242,7 +242,7 @@ class MTLTexture : public Texture {
id<MTLBuffer> vert_buffer_mtl_;
int vert_buffer_offset_;
- /* Core parameters and subresources. */
+ /* Core parameters and sub-resources. */
eGPUTextureUsage gpu_image_usage_flags_;
/* Whether the texture's properties or state has changed (e.g. mipmap range), and re-baking of
diff --git a/source/blender/gpu/metal/mtl_texture.mm b/source/blender/gpu/metal/mtl_texture.mm
index df3efdd12e7..7ec3d390416 100644
--- a/source/blender/gpu/metal/mtl_texture.mm
+++ b/source/blender/gpu/metal/mtl_texture.mm
@@ -563,7 +563,7 @@ void gpu::MTLTexture::update_sub(
return;
}
- /* Check Format writeability. */
+ /* Check Format write-ability. */
if (mtl_format_get_writeable_view_format(destination_format) == MTLPixelFormatInvalid) {
MTL_LOG_ERROR(
"[Error]: Updating texture -- destination MTLPixelFormat '%d' does not support write "
@@ -1769,8 +1769,8 @@ void gpu::MTLTexture::ensure_baked()
/* CUBE TEXTURES */
case GPU_TEXTURE_CUBE:
case GPU_TEXTURE_CUBE_ARRAY: {
- /* Note: For a cubemap 'Texture::d_' refers to total number of faces, not just array slices
- */
+ /* NOTE: For a cube-map 'Texture::d_' refers to total number of faces,
+ * not just array slices. */
BLI_assert(this->w_ > 0 && this->h_ > 0);
this->texture_descriptor_ = [[MTLTextureDescriptor alloc] init];
this->texture_descriptor_.pixelFormat = mtl_format;
diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh
index 2dde8d6c86b..e5b879f1f15 100644
--- a/source/blender/gpu/opengl/gl_texture.hh
+++ b/source/blender/gpu/opengl/gl_texture.hh
@@ -363,7 +363,7 @@ inline GLenum to_gl_data_format(eGPUTextureFormat format)
}
/**
- * Assume Unorm / Float target. Used with #glReadPixels.
+ * Assume UNORM/Float target. Used with #glReadPixels.
*/
inline GLenum channel_len_to_gl(int channel_len)
{
diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc
index 04f60f10d41..cfcf77fe705 100644
--- a/source/blender/gpu/opengl/gl_vertex_array.cc
+++ b/source/blender/gpu/opengl/gl_vertex_array.cc
@@ -39,8 +39,8 @@ static uint16_t vbo_bind(const ShaderInterface *interface,
const GPUVertAttr *a = &format->attrs[a_idx];
if (format->deinterleaved) {
- offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].sz) * v_len;
- stride = a->sz;
+ offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].size) * v_len;
+ stride = a->size;
}
else {
offset = a->offset;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_color_ramp.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_color_ramp.glsl
index 17240da4050..17240da4050 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_color_ramp.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_color_ramp.glsl
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_color_util.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl
index a5c3a990d90..fe89985ae7f 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_color_util.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl
@@ -90,6 +90,56 @@ void hsv_to_rgb(vec4 hsv, out vec4 outcol)
outcol = vec4(rgb, hsv.w);
}
+void rgb_to_hsl(vec4 rgb, out vec4 outcol)
+{
+ float cmax, cmin, h, s, l;
+
+ cmax = max(rgb[0], max(rgb[1], rgb[2]));
+ cmin = min(rgb[0], min(rgb[1], rgb[2]));
+ l = min(1.0, (cmax + cmin) / 2.0);
+
+ if (cmax == cmin) {
+ h = s = 0.0; /* achromatic */
+ }
+ else {
+ float cdelta = cmax - cmin;
+ s = l > 0.5 ? cdelta / (2.0 - cmax - cmin) : cdelta / (cmax + cmin);
+ if (cmax == rgb[0]) {
+ h = (rgb[1] - rgb[2]) / cdelta + (rgb[1] < rgb[2] ? 6.0 : 0.0);
+ }
+ else if (cmax == rgb[1]) {
+ h = (rgb[2] - rgb[0]) / cdelta + 2.0;
+ }
+ else {
+ h = (rgb[0] - rgb[1]) / cdelta + 4.0;
+ }
+ }
+ h /= 6.0;
+
+ outcol = vec4(h, s, l, rgb.w);
+}
+
+void hsl_to_rgb(vec4 hsl, out vec4 outcol)
+{
+ float nr, ng, nb, chroma, h, s, l;
+
+ h = hsl[0];
+ s = hsl[1];
+ l = hsl[2];
+
+ nr = abs(h * 6.0 - 3.0) - 1.0;
+ ng = 2.0 - abs(h * 6.0 - 2.0);
+ nb = 2.0 - abs(h * 6.0 - 4.0);
+
+ nr = clamp(nr, 0.0, 1.0);
+ nb = clamp(nb, 0.0, 1.0);
+ ng = clamp(ng, 0.0, 1.0);
+
+ chroma = (1.0 - abs(2.0 * l - 1.0)) * s;
+
+ outcol = vec4((nr - 0.5) * chroma + l, (ng - 0.5) * chroma + l, (nb - 0.5) * chroma + l, hsl.w);
+}
+
void color_alpha_clear(vec4 color, out vec4 result)
{
result = vec4(color.rgb, 1.0);
diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl
new file mode 100644
index 00000000000..8948ed77557
--- /dev/null
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl
@@ -0,0 +1,162 @@
+vec4 white_balance(vec4 color, vec4 black_level, vec4 white_level)
+{
+ vec4 range = max(white_level - black_level, vec4(1e-5f));
+ return (color - black_level) / range;
+}
+
+float extrapolate_if_needed(float parameter, float value, float start_slope, float end_slope)
+{
+ if (parameter < 0.0) {
+ return value + parameter * start_slope;
+ }
+
+ if (parameter > 1.0) {
+ return value + (parameter - 1.0) * end_slope;
+ }
+
+ return value;
+}
+
+/* Same as extrapolate_if_needed but vectorized. */
+vec3 extrapolate_if_needed(vec3 parameters, vec3 values, vec3 start_slopes, vec3 end_slopes)
+{
+ vec3 end_or_zero_slopes = mix(vec3(0.0), end_slopes, greaterThan(parameters, vec3(1.0)));
+ vec3 slopes = mix(end_or_zero_slopes, start_slopes, lessThan(parameters, vec3(0.0)));
+ parameters = parameters - mix(vec3(0.0), vec3(1.0), greaterThan(parameters, vec3(1.0)));
+ return values + parameters * slopes;
+}
+
+/* Curve maps are stored in sampler objects that are evaluated in the [0, 1] range, so normalize
+ * parameters accordingly. */
+#define NORMALIZE_PARAMETER(parameter, minimum, range) ((parameter - minimum) * range)
+
+void curves_combined_rgb(float factor,
+ vec4 color,
+ vec4 black_level,
+ vec4 white_level,
+ sampler1DArray curve_map,
+ const float layer,
+ vec4 range_minimums,
+ vec4 range_dividers,
+ vec4 start_slopes,
+ vec4 end_slopes,
+ out vec4 result)
+{
+ vec4 balanced = white_balance(color, black_level, white_level);
+
+ /* First, evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the
+ * UI. */
+ vec3 parameters = NORMALIZE_PARAMETER(balanced.rgb, range_minimums.aaa, range_dividers.aaa);
+ result.r = texture(curve_map, vec2(parameters.x, layer)).a;
+ result.g = texture(curve_map, vec2(parameters.y, layer)).a;
+ result.b = texture(curve_map, vec2(parameters.z, layer)).a;
+
+ /* Then, extrapolate if needed. */
+ result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.aaa, end_slopes.aaa);
+
+ /* Then, evaluate each channel on its curve map. */
+ parameters = NORMALIZE_PARAMETER(result.rgb, range_minimums.rgb, range_dividers.rgb);
+ result.r = texture(curve_map, vec2(parameters.r, layer)).r;
+ result.g = texture(curve_map, vec2(parameters.g, layer)).g;
+ result.b = texture(curve_map, vec2(parameters.b, layer)).b;
+
+ /* Then, extrapolate again if needed. */
+ result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.rgb, end_slopes.rgb);
+ result.a = color.a;
+
+ result = mix(color, result, factor);
+}
+
+void curves_combined_only(float factor,
+ vec4 color,
+ vec4 black_level,
+ vec4 white_level,
+ sampler1DArray curve_map,
+ const float layer,
+ float range_minimum,
+ float range_divider,
+ float start_slope,
+ float end_slope,
+ out vec4 result)
+{
+ vec4 balanced = white_balance(color, black_level, white_level);
+
+ /* Evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the
+ * UI. */
+ vec3 parameters = NORMALIZE_PARAMETER(balanced.rgb, range_minimum, range_divider);
+ result.r = texture(curve_map, vec2(parameters.x, layer)).a;
+ result.g = texture(curve_map, vec2(parameters.y, layer)).a;
+ result.b = texture(curve_map, vec2(parameters.z, layer)).a;
+
+ /* Then, extrapolate if needed. */
+ result.rgb = extrapolate_if_needed(parameters, result.rgb, vec3(start_slope), vec3(end_slope));
+ result.a = color.a;
+
+ result = mix(color, result, factor);
+}
+
+void curves_vector(vec3 vector,
+ sampler1DArray curve_map,
+ const float layer,
+ vec3 range_minimums,
+ vec3 range_dividers,
+ vec3 start_slopes,
+ vec3 end_slopes,
+ out vec3 result)
+{
+ /* Evaluate each component on its curve map. */
+ vec3 parameters = NORMALIZE_PARAMETER(vector, range_minimums, range_dividers);
+ result.x = texture(curve_map, vec2(parameters.x, layer)).x;
+ result.y = texture(curve_map, vec2(parameters.y, layer)).y;
+ result.z = texture(curve_map, vec2(parameters.z, layer)).z;
+
+ /* Then, extrapolate if needed. */
+ result = extrapolate_if_needed(parameters, result, start_slopes, end_slopes);
+}
+
+void curves_vector_mixed(float factor,
+ vec3 vector,
+ sampler1DArray curve_map,
+ const float layer,
+ vec3 range_minimums,
+ vec3 range_dividers,
+ vec3 start_slopes,
+ vec3 end_slopes,
+ out vec3 result)
+{
+ curves_vector(
+ vector, curve_map, layer, range_minimums, range_dividers, start_slopes, end_slopes, result);
+ result = mix(vector, result, factor);
+}
+
+void curves_float(float value,
+ sampler1DArray curve_map,
+ const float layer,
+ float range_minimum,
+ float range_divider,
+ float start_slope,
+ float end_slope,
+ out float result)
+{
+ /* Evaluate the normalized value on the first curve map. */
+ float parameter = NORMALIZE_PARAMETER(value, range_minimum, range_divider);
+ result = texture(curve_map, vec2(parameter, layer)).x;
+
+ /* Then, extrapolate if needed. */
+ result = extrapolate_if_needed(parameter, result, start_slope, end_slope);
+}
+
+void curves_float_mixed(float factor,
+ float value,
+ sampler1DArray curve_map,
+ const float layer,
+ float range_minimum,
+ float range_divider,
+ float start_slope,
+ float end_slope,
+ out float result)
+{
+ curves_float(
+ value, curve_map, layer, range_minimum, range_divider, start_slope, end_slope, result);
+ result = mix(value, result, factor);
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_hash.glsl
index 32d61f06a65..32d61f06a65 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_hash.glsl
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_math.glsl
index 0948ce2c9fa..5f640f64056 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_math.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
void math_add(float a, float b, float c, out float result)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl
index 91a8996939a..124654963fd 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl
@@ -139,75 +139,3 @@ mat3 euler_to_mat3(vec3 euler)
mat[2][2] = cy * cx;
return mat;
}
-
-void normal_transform_object_to_world(vec3 vin, out vec3 vout)
-{
- vout = normal_object_to_world(vin);
-}
-
-void normal_transform_world_to_object(vec3 vin, out vec3 vout)
-{
- vout = normal_world_to_object(vin);
-}
-
-void direction_transform_object_to_world(vec3 vin, out vec3 vout)
-{
- vout = transform_direction(ModelMatrix, vin);
-}
-
-void direction_transform_object_to_view(vec3 vin, out vec3 vout)
-{
- vout = transform_direction(ModelMatrix, vin);
- vout = transform_direction(ViewMatrix, vout);
-}
-
-void direction_transform_view_to_world(vec3 vin, out vec3 vout)
-{
- vout = transform_direction(ViewMatrixInverse, vin);
-}
-
-void direction_transform_view_to_object(vec3 vin, out vec3 vout)
-{
- vout = transform_direction(ViewMatrixInverse, vin);
- vout = transform_direction(ModelMatrixInverse, vout);
-}
-
-void direction_transform_world_to_view(vec3 vin, out vec3 vout)
-{
- vout = transform_direction(ViewMatrix, vin);
-}
-
-void direction_transform_world_to_object(vec3 vin, out vec3 vout)
-{
- vout = transform_direction(ModelMatrixInverse, vin);
-}
-
-void point_transform_object_to_world(vec3 vin, out vec3 vout)
-{
- vout = point_object_to_world(vin);
-}
-
-void point_transform_object_to_view(vec3 vin, out vec3 vout)
-{
- vout = point_object_to_view(vin);
-}
-
-void point_transform_view_to_world(vec3 vin, out vec3 vout)
-{
- vout = point_view_to_world(vin);
-}
-
-void point_transform_view_to_object(vec3 vin, out vec3 vout)
-{
- vout = point_view_to_object(vin);
-}
-
-void point_transform_world_to_view(vec3 vin, out vec3 vout)
-{
- vout = point_world_to_view(vin);
-}
-
-void point_transform_world_to_object(vec3 vin, out vec3 vout)
-{
- vout = point_world_to_object(vin);
-}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl
index 157a6a27c15..f9652f1150b 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl
@@ -1,4 +1,4 @@
-#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
void mix_blend(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
diff --git a/source/blender/gpu/shaders/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..8796c99629e 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -109,6 +109,14 @@ struct ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[I
/**
*
+ * \attention Defined in readimage.c
+ */
+struct ImBuf *IMB_thumb_load_image(const char *filepath,
+ const size_t max_thumb_size,
+ char colorspace[IM_MAX_SPACE]);
+
+/**
+ *
* \attention Defined in allocimbuf.c
*/
void IMB_freeImBuf(struct ImBuf *ibuf);
diff --git a/source/blender/imbuf/intern/IMB_filetype.h b/source/blender/imbuf/intern/IMB_filetype.h
index 31f8b3a9505..67d1aefeacb 100644
--- a/source/blender/imbuf/intern/IMB_filetype.h
+++ b/source/blender/imbuf/intern/IMB_filetype.h
@@ -36,6 +36,17 @@ typedef struct ImFileType {
char colorspace[IM_MAX_SPACE]);
/** Load an image from a file. */
struct ImBuf *(*load_filepath)(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]);
+ /**
+ * Load/Create a thumbnail image from a filepath. `max_thumb_size` is maximum size of either
+ * dimension, so can return less on either or both. Should, if possible and performant, return
+ * dimensions of the full-size image in r_width & r_height.
+ */
+ struct ImBuf *(*load_filepath_thumbnail)(const char *filepath,
+ const int flags,
+ const size_t max_thumb_size,
+ char colorspace[IM_MAX_SPACE],
+ size_t *r_width,
+ size_t *r_height);
/** Save to a file (or memory if #IB_mem is set in `flags` and the format supports it). */
bool (*save)(struct ImBuf *ibuf, const char *filepath, int flags);
void (*load_tile)(struct ImBuf *ibuf,
@@ -143,6 +154,12 @@ struct ImBuf *imb_load_jpeg(const unsigned char *buffer,
size_t size,
int flags,
char colorspace[IM_MAX_SPACE]);
+struct ImBuf *imb_thumbnail_jpeg(const char *filepath,
+ const int flags,
+ const size_t max_thumb_size,
+ char colorspace[IM_MAX_SPACE],
+ size_t *r_width,
+ size_t *r_height);
/** \} */
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index 096089d4c41..0052ce19aa1 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -423,7 +423,7 @@ static int startavi(struct anim *anim)
anim->cur_position = 0;
# if 0
- printf("x:%d y:%d size:%d interl:%d dur:%d\n",
+ printf("x:%d y:%d size:%d interlace:%d dur:%d\n",
anim->x,
anim->y,
anim->framesize,
diff --git a/source/blender/imbuf/intern/filetype.c b/source/blender/imbuf/intern/filetype.c
index 548bc9e120c..74042ef75be 100644
--- a/source/blender/imbuf/intern/filetype.c
+++ b/source/blender/imbuf/intern/filetype.c
@@ -33,6 +33,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.is_a = imb_is_a_jpeg,
.load = imb_load_jpeg,
.load_filepath = NULL,
+ .load_filepath_thumbnail = imb_thumbnail_jpeg,
.save = imb_savejpeg,
.load_tile = NULL,
.flag = 0,
@@ -45,6 +46,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.is_a = imb_is_a_png,
.load = imb_loadpng,
.load_filepath = NULL,
+ .load_filepath_thumbnail = NULL,
.save = imb_savepng,
.load_tile = NULL,
.flag = 0,
@@ -57,6 +59,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.is_a = imb_is_a_bmp,
.load = imb_bmp_decode,
.load_filepath = NULL,
+ .load_filepath_thumbnail = NULL,
.save = imb_savebmp,
.load_tile = NULL,
.flag = 0,
@@ -69,6 +72,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.is_a = imb_is_a_targa,
.load = imb_loadtarga,
.load_filepath = NULL,
+ .load_filepath_thumbnail = NULL,
.save = imb_savetarga,
.load_tile = NULL,
.flag = 0,
@@ -81,6 +85,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.is_a = imb_is_a_iris,
.load = imb_loadiris,
.load_filepath = NULL,
+ .load_filepath_thumbnail = NULL,
.save = imb_saveiris,
.load_tile = NULL,
.flag = 0,
@@ -94,6 +99,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.is_a = imb_is_a_dpx,
.load = imb_load_dpx,
.load_filepath = NULL,
+ .load_filepath_thumbnail = NULL,
.save = imb_save_dpx,
.load_tile = NULL,
.flag = IM_FTYPE_FLOAT,
@@ -106,6 +112,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.is_a = imb_is_a_cineon,
.load = imb_load_cineon,
.load_filepath = NULL,
+ .load_filepath_thumbnail = NULL,
.save = imb_save_cineon,
.load_tile = NULL,
.flag = IM_FTYPE_FLOAT,
@@ -120,6 +127,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.is_a = imb_is_a_tiff,
.load = imb_loadtiff,
.load_filepath = NULL,
+ .load_filepath_thumbnail = NULL,
.save = imb_savetiff,
.load_tile = imb_loadtiletiff,
.flag = 0,
@@ -134,6 +142,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.is_a = imb_is_a_hdr,
.load = imb_loadhdr,
.load_filepath = NULL,
+ .load_filepath_thumbnail = NULL,
.save = imb_savehdr,
.load_tile = NULL,
.flag = IM_FTYPE_FLOAT,
@@ -148,6 +157,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.is_a = imb_is_a_openexr,
.load = imb_load_openexr,
.load_filepath = NULL,
+ .load_filepath_thumbnail = NULL,
.save = imb_save_openexr,
.load_tile = NULL,
.flag = IM_FTYPE_FLOAT,
@@ -162,6 +172,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.is_a = imb_is_a_jp2,
.load = imb_load_jp2,
.load_filepath = NULL,
+ .load_filepath_thumbnail = NULL,
.save = imb_save_jp2,
.load_tile = NULL,
.flag = IM_FTYPE_FLOAT,
@@ -176,6 +187,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.is_a = imb_is_a_dds,
.load = imb_load_dds,
.load_filepath = NULL,
+ .load_filepath_thumbnail = NULL,
.save = NULL,
.load_tile = NULL,
.flag = 0,
@@ -190,6 +202,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.is_a = imb_is_a_photoshop,
.load = NULL,
.load_filepath = imb_load_photoshop,
+ .load_filepath_thumbnail = NULL,
.save = NULL,
.load_tile = NULL,
.flag = IM_FTYPE_FLOAT,
@@ -204,6 +217,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.is_a = imb_is_a_webp,
.load = imb_loadwebp,
.load_filepath = NULL,
+ .load_filepath_thumbnail = NULL,
.save = imb_savewebp,
.load_tile = NULL,
.flag = 0,
@@ -211,7 +225,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.default_save_role = COLOR_ROLE_DEFAULT_BYTE,
},
#endif
- {NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0},
+ {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0},
};
const ImFileType *IMB_FILE_TYPES_LAST = &IMB_FILE_TYPES[ARRAY_SIZE(IMB_FILE_TYPES) - 1];
diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c
index 6fb1fb52153..cffa61977f7 100644
--- a/source/blender/imbuf/intern/jpeg.c
+++ b/source/blender/imbuf/intern/jpeg.c
@@ -39,7 +39,11 @@ static void skip_input_data(j_decompress_ptr cinfo, long num_bytes);
static void term_source(j_decompress_ptr cinfo);
static void memory_source(j_decompress_ptr cinfo, const unsigned char *buffer, size_t size);
static boolean handle_app1(j_decompress_ptr cinfo);
-static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int flags);
+static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo,
+ int flags,
+ int max_size,
+ size_t *r_width,
+ size_t *r_height);
static const uchar jpeg_default_quality = 75;
static uchar ibuf_quality;
@@ -246,7 +250,11 @@ static boolean handle_app1(j_decompress_ptr cinfo)
return true;
}
-static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int flags)
+static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo,
+ int flags,
+ int max_size,
+ size_t *r_width,
+ size_t *r_height)
{
JSAMPARRAY row_pointer;
JSAMPLE *buffer = NULL;
@@ -264,16 +272,34 @@ static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int fla
jpeg_save_markers(cinfo, JPEG_COM, 0xffff);
if (jpeg_read_header(cinfo, false) == JPEG_HEADER_OK) {
- x = cinfo->image_width;
- y = cinfo->image_height;
depth = cinfo->num_components;
if (cinfo->jpeg_color_space == JCS_YCCK) {
cinfo->out_color_space = JCS_CMYK;
}
+ if (r_width) {
+ *r_width = cinfo->image_width;
+ }
+ if (r_height) {
+ *r_height = cinfo->image_height;
+ }
+
+ if (max_size > 0) {
+ /* `libjpeg` can more quickly decompress while scaling down to 1/2, 1/4, 1/8,
+ * while `libjpeg-turbo` can also do 3/8, 5/8, etc. But max is 1/8. */
+ float scale = (float)max_size / MAX2(cinfo->image_width, cinfo->image_height);
+ cinfo->scale_denom = 8;
+ cinfo->scale_num = max_uu(1, min_uu(8, ceill(scale * (float)cinfo->scale_denom)));
+ cinfo->dct_method = JDCT_FASTEST;
+ cinfo->dither_mode = JDITHER_ORDERED;
+ }
+
jpeg_start_decompress(cinfo);
+ x = cinfo->output_width;
+ y = cinfo->output_height;
+
if (flags & IB_test) {
jpeg_abort_decompress(cinfo);
ibuf = IMB_allocImBuf(x, y, 8 * depth, 0);
@@ -449,11 +475,92 @@ ImBuf *imb_load_jpeg(const unsigned char *buffer,
jpeg_create_decompress(cinfo);
memory_source(cinfo, buffer, size);
- ibuf = ibJpegImageFromCinfo(cinfo, flags);
+ ibuf = ibJpegImageFromCinfo(cinfo, flags, -1, NULL, NULL);
+
+ return ibuf;
+}
+
+/* Defines for JPEG Header markers and segment size. */
+#define JPEG_MARKER_MSB (0xFF)
+#define JPEG_MARKER_SOI (0xD8)
+#define JPEG_MARKER_APP1 (0xE1)
+#define JPEG_APP1_MAX (1 << 16)
+
+struct ImBuf *imb_thumbnail_jpeg(const char *filepath,
+ const int flags,
+ const size_t max_thumb_size,
+ char colorspace[IM_MAX_SPACE],
+ size_t *r_width,
+ size_t *r_height)
+{
+ struct jpeg_decompress_struct _cinfo, *cinfo = &_cinfo;
+ struct my_error_mgr jerr;
+ FILE *infile = NULL;
+
+ colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
+
+ cinfo->err = jpeg_std_error(&jerr.pub);
+ jerr.pub.error_exit = jpeg_error;
+
+ /* Establish the setjmp return context for my_error_exit to use. */
+ if (setjmp(jerr.setjmp_buffer)) {
+ /* If we get here, the JPEG code has signaled an error.
+ * We need to clean up the JPEG object, close the input file, and return.
+ */
+ jpeg_destroy_decompress(cinfo);
+ return NULL;
+ }
+
+ if ((infile = BLI_fopen(filepath, "rb")) == NULL) {
+ fprintf(stderr, "can't open %s\n", filepath);
+ return NULL;
+ }
+
+ /* If file contains an embedded thumbnail, let's return that instead. */
+
+ if ((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI) &&
+ (fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_APP1)) {
+ /* This is a JPEG in EXIF format (SOI + APP1), not JFIF (SOI + APP0). */
+ unsigned int i = JPEG_APP1_MAX;
+ /* All EXIF data is within this 64K header segment. Skip ahead until next SOI for thumbnail. */
+ while (!((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI)) &&
+ !feof(infile) && i--)
+ ;
+ if (i > 0 && !feof(infile)) {
+ /* We found a JPEG thumbnail inside this image. */
+ ImBuf *ibuf = NULL;
+ uchar *buffer = MEM_callocN(JPEG_APP1_MAX, "thumbbuffer");
+ /* Just put SOI directly in buffer rather than seeking back 2 bytes. */
+ buffer[0] = JPEG_MARKER_MSB;
+ buffer[1] = JPEG_MARKER_SOI;
+ if (fread(buffer + 2, JPEG_APP1_MAX - 2, 1, infile) == 1) {
+ ibuf = imb_load_jpeg(buffer, JPEG_APP1_MAX, flags, colorspace);
+ }
+ MEM_SAFE_FREE(buffer);
+ if (ibuf) {
+ fclose(infile);
+ return ibuf;
+ }
+ }
+ }
+
+ /* No embedded thumbnail found, so let's create a new one. */
+
+ fseek(infile, 0, SEEK_SET);
+ jpeg_create_decompress(cinfo);
+
+ jpeg_stdio_src(cinfo, infile);
+ ImBuf *ibuf = ibJpegImageFromCinfo(cinfo, flags, max_thumb_size, r_width, r_height);
+ fclose(infile);
return ibuf;
}
+#undef JPEG_MARKER_MSB
+#undef JPEG_MARKER_SOI
+#undef JPEG_MARKER_APP1
+#undef JPEG_APP1_MAX
+
static void write_jpeg(struct jpeg_compress_struct *cinfo, struct ImBuf *ibuf)
{
JSAMPLE *buffer = NULL;
diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c
index df41c0ca757..805a7e8d687 100644
--- a/source/blender/imbuf/intern/readimage.c
+++ b/source/blender/imbuf/intern/readimage.c
@@ -22,6 +22,8 @@
#include "IMB_filetype.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+#include "IMB_metadata.h"
+#include "IMB_thumbs.h"
#include "imbuf.h"
#include "IMB_colormanagement.h"
@@ -234,6 +236,61 @@ ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_S
return ibuf;
}
+struct ImBuf *IMB_thumb_load_image(const char *filepath,
+ size_t max_thumb_size,
+ char colorspace[IM_MAX_SPACE])
+{
+ const ImFileType *type = IMB_file_type_from_ftype(IMB_ispic_type(filepath));
+ if (type == NULL) {
+ return NULL;
+ }
+
+ ImBuf *ibuf = NULL;
+ int flags = IB_rect | IB_metadata;
+ /* Size of the original image. */
+ size_t width = 0;
+ size_t height = 0;
+
+ char effective_colorspace[IM_MAX_SPACE] = "";
+ if (colorspace) {
+ BLI_strncpy(effective_colorspace, colorspace, sizeof(effective_colorspace));
+ }
+
+ if (type->load_filepath_thumbnail) {
+ ibuf = type->load_filepath_thumbnail(
+ filepath, flags, max_thumb_size, colorspace, &width, &height);
+ }
+ else {
+ /* Skip images of other types if over 100MB. */
+ const size_t file_size = BLI_file_size(filepath);
+ if (file_size != -1 && file_size > THUMB_SIZE_MAX) {
+ return NULL;
+ }
+ ibuf = IMB_loadiffname(filepath, flags, colorspace);
+ if (ibuf) {
+ width = ibuf->x;
+ height = ibuf->y;
+ }
+ }
+
+ if (ibuf) {
+ imb_handle_alpha(ibuf, flags, colorspace, effective_colorspace);
+
+ if (width > 0 && height > 0) {
+ /* Save dimensions of original image into the thumbnail metadata. */
+ char cwidth[40];
+ char cheight[40];
+ SNPRINTF(cwidth, "%zu", width);
+ SNPRINTF(cheight, "%zu", height);
+ IMB_metadata_ensure(&ibuf->metadata);
+ IMB_metadata_set_field(ibuf->metadata, "Thumb::Image::Width", cwidth);
+ IMB_metadata_set_field(ibuf->metadata, "Thumb::Image::Height", cheight);
+ }
+ }
+
+ return ibuf;
+}
+
ImBuf *IMB_testiffname(const char *filepath, int flags)
{
ImBuf *ibuf;
diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c
index 37734ebacb2..51951aa9605 100644
--- a/source/blender/imbuf/intern/thumbs.c
+++ b/source/blender/imbuf/intern/thumbs.c
@@ -318,12 +318,8 @@ static ImBuf *thumb_create_ex(const char *file_path,
char tpath[FILE_MAX];
char tdir[FILE_MAX];
char temp[FILE_MAX];
- char mtime[40] = "0"; /* in case we can't stat the file */
- char cwidth[40] = "0"; /* in case images have no data */
- char cheight[40] = "0";
+ char mtime[40] = "0"; /* in case we can't stat the file */
short tsize = 128;
- short ex, ey;
- float scaledx, scaledy;
BLI_stat_t info;
switch (size) {
@@ -340,15 +336,6 @@ static ImBuf *thumb_create_ex(const char *file_path,
return NULL; /* unknown size */
}
- /* exception, skip images over 100mb */
- if (source == THB_SOURCE_IMAGE) {
- const size_t file_size = BLI_file_size(file_path);
- if (file_size != -1 && file_size > THUMB_SIZE_MAX) {
- // printf("file too big: %d, skipping %s\n", (int)size, file_path);
- return NULL;
- }
- }
-
if (get_thumb_dir(tdir, size)) {
BLI_snprintf(tpath, FILE_MAX, "%s%s", tdir, thumb);
// thumb[8] = '\0'; /* shorten for tempname, not needed anymore */
@@ -368,7 +355,7 @@ static ImBuf *thumb_create_ex(const char *file_path,
if (img == NULL) {
switch (source) {
case THB_SOURCE_IMAGE:
- img = IMB_loadiffname(file_path, IB_rect | IB_metadata, NULL);
+ img = IMB_thumb_load_image(file_path, tsize, NULL);
break;
case THB_SOURCE_BLEND:
img = IMB_thumb_load_blend(file_path, blen_group, blen_id);
@@ -385,8 +372,6 @@ static ImBuf *thumb_create_ex(const char *file_path,
if (BLI_stat(file_path, &info) != -1) {
BLI_snprintf(mtime, sizeof(mtime), "%ld", (long int)info.st_mtime);
}
- BLI_snprintf(cwidth, sizeof(cwidth), "%d", img->x);
- BLI_snprintf(cheight, sizeof(cheight), "%d", img->y);
}
}
else if (THB_SOURCE_MOVIE == source) {
@@ -411,28 +396,20 @@ static ImBuf *thumb_create_ex(const char *file_path,
return NULL;
}
- if (img->x > img->y) {
- scaledx = (float)tsize;
- scaledy = ((float)img->y / (float)img->x) * tsize;
- }
- else {
- scaledy = (float)tsize;
- scaledx = ((float)img->x / (float)img->y) * tsize;
- }
- /* Scaling down must never assign zero width/height, see: T89868. */
- ex = MAX2(1, (short)scaledx);
- ey = MAX2(1, (short)scaledy);
-
- /* save some time by only scaling byte buf */
- if (img->rect_float) {
- if (img->rect == NULL) {
- IMB_rect_from_float(img);
+ if (img->x > tsize || img->y > tsize) {
+ float scale = MIN2((float)tsize / (float)img->x, (float)tsize / (float)img->y);
+ /* Scaling down must never assign zero width/height, see: T89868. */
+ short ex = MAX2(1, (short)(img->x * scale));
+ short ey = MAX2(1, (short)(img->y * scale));
+ /* Save some time by only scaling byte buf */
+ if (img->rect_float) {
+ if (img->rect == NULL) {
+ IMB_rect_from_float(img);
+ }
+ imb_freerectfloatImBuf(img);
}
-
- imb_freerectfloatImBuf(img);
+ IMB_scaleImBuf(img, ex, ey);
}
-
- IMB_scaleImBuf(img, ex, ey);
}
BLI_snprintf(desc, sizeof(desc), "Thumbnail for %s", uri);
IMB_metadata_ensure(&img->metadata);
@@ -443,10 +420,6 @@ static ImBuf *thumb_create_ex(const char *file_path,
if (use_hash) {
IMB_metadata_set_field(img->metadata, "X-Blender::Hash", hash);
}
- if (ELEM(source, THB_SOURCE_IMAGE, THB_SOURCE_BLEND, THB_SOURCE_FONT)) {
- IMB_metadata_set_field(img->metadata, "Thumb::Image::Width", cwidth);
- IMB_metadata_set_field(img->metadata, "Thumb::Image::Height", cheight);
- }
img->ftype = IMB_FTYPE_PNG;
img->planes = 32;
diff --git a/source/blender/io/alembic/exporter/abc_export_capi.cc b/source/blender/io/alembic/exporter/abc_export_capi.cc
index c1ff7df0c37..edaf53b3efa 100644
--- a/source/blender/io/alembic/exporter/abc_export_capi.cc
+++ b/source/blender/io/alembic/exporter/abc_export_capi.cc
@@ -115,7 +115,7 @@ static void export_startjob(void *customdata,
return;
}
- ABCHierarchyIterator iter(data->depsgraph, abc_archive.get(), data->params);
+ ABCHierarchyIterator iter(data->bmain, data->depsgraph, abc_archive.get(), data->params);
if (export_animation) {
CLOG_INFO(&LOG, 2, "Exporting animation");
diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
index d33adfdb4b9..ab62694b802 100644
--- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
+++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
@@ -26,10 +26,11 @@
namespace blender::io::alembic {
-ABCHierarchyIterator::ABCHierarchyIterator(Depsgraph *depsgraph,
+ABCHierarchyIterator::ABCHierarchyIterator(Main *bmain,
+ Depsgraph *depsgraph,
ABCArchive *abc_archive,
const AlembicExportParams &params)
- : 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 &params_;
public:
- ABCHierarchyIterator(Depsgraph *depsgraph,
+ ABCHierarchyIterator(Main *bmain,
+ Depsgraph *depsgraph,
ABCArchive *abc_archive_,
const AlembicExportParams &params);
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_path_util.hh b/source/blender/io/common/IO_path_util.hh
index ac2f935523e..eeb5a9dbcfe 100644
--- a/source/blender/io/common/IO_path_util.hh
+++ b/source/blender/io/common/IO_path_util.hh
@@ -1,8 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
-#include "BLI_string_ref.hh"
#include "BLI_set.hh"
+#include "BLI_string_ref.hh"
#include "IO_path_util_types.h"
@@ -14,7 +14,7 @@ namespace blender::io {
*
* When PATH_REFERENCE_COPY mode is used, the file path pair (source
* path, destination path) is added to the `copy_set`.
- *
+ *
* Equivalent of bpy_extras.io_utils.path_reference.
*/
std::string path_reference(StringRefNull filepath,
@@ -26,4 +26,4 @@ std::string path_reference(StringRefNull filepath,
/** Execute copying files of path_reference. */
void path_reference_copy(const Set<std::pair<std::string, std::string>> &copy_set);
-} // namespace blender::io
+} // namespace blender::io
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/path_util.cc b/source/blender/io/common/intern/path_util.cc
index 2b9a1d67b44..902cf552bf0 100644
--- a/source/blender/io/common/intern/path_util.cc
+++ b/source/blender/io/common/intern/path_util.cc
@@ -28,7 +28,8 @@ std::string path_reference(StringRefNull filepath,
}
else if (mode == PATH_REFERENCE_COPY) {
char filepath_cpy[PATH_MAX];
- BLI_path_join(filepath_cpy, PATH_MAX, base_dst.c_str(), BLI_path_basename(filepath_abs), nullptr);
+ BLI_path_join(
+ filepath_cpy, PATH_MAX, base_dst.c_str(), BLI_path_basename(filepath_abs), nullptr);
copy_set->add(std::make_pair(filepath_abs, filepath_cpy));
BLI_strncpy(filepath_abs, filepath_cpy, PATH_MAX);
mode = PATH_REFERENCE_RELATIVE;
diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt
index e2e959814fa..7a7c95b29f9 100644
--- a/source/blender/io/usd/CMakeLists.txt
+++ b/source/blender/io/usd/CMakeLists.txt
@@ -69,6 +69,7 @@ set(SRC
intern/usd_writer_mesh.cc
intern/usd_writer_metaball.cc
intern/usd_writer_transform.cc
+ intern/usd_writer_volume.cc
intern/usd_reader_camera.cc
intern/usd_reader_curve.cc
@@ -95,6 +96,7 @@ set(SRC
intern/usd_writer_mesh.h
intern/usd_writer_metaball.h
intern/usd_writer_transform.h
+ intern/usd_writer_volume.h
intern/usd_reader_camera.h
intern/usd_reader_curve.h
@@ -119,8 +121,15 @@ list(APPEND LIB
${BOOST_LIBRARIES}
)
-list(APPEND LIB
-)
+if(WITH_OPENVDB)
+ add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS})
+ list(APPEND INC_SYS
+ ${OPENVDB_INCLUDE_DIRS}
+ )
+ list(APPEND LIB
+ ${OPENVDB_LIBRARIES}
+ )
+endif()
blender_add_lib(bf_usd "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/io/usd/intern/usd_capi_export.cc b/source/blender/io/usd/intern/usd_capi_export.cc
index d7c1a5adc7c..c52d791f3b3 100644
--- a/source/blender/io/usd/intern/usd_capi_export.cc
+++ b/source/blender/io/usd/intern/usd_capi_export.cc
@@ -97,7 +97,7 @@ static void export_startjob(void *customdata,
usd_stage->SetEndTimeCode(scene->r.efra);
}
- USDHierarchyIterator iter(data->depsgraph, usd_stage, data->params);
+ USDHierarchyIterator iter(data->bmain, data->depsgraph, usd_stage, data->params);
if (data->params.export_animation) {
/* Writing the animated frames is not 100% of the work, but it's our best guess. */
diff --git a/source/blender/io/usd/intern/usd_exporter_context.h b/source/blender/io/usd/intern/usd_exporter_context.h
index 5a3f94d01f8..a636d849296 100644
--- a/source/blender/io/usd/intern/usd_exporter_context.h
+++ b/source/blender/io/usd/intern/usd_exporter_context.h
@@ -8,12 +8,14 @@
#include <pxr/usd/usd/common.h>
struct Depsgraph;
+struct Main;
namespace blender::io::usd {
class USDHierarchyIterator;
struct USDExporterContext {
+ Main *bmain;
Depsgraph *depsgraph;
const pxr::UsdStageRefPtr stage;
const pxr::SdfPath usd_path;
diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc
index 7a0d896fb3e..51261c4d91e 100644
--- a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc
+++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc
@@ -10,6 +10,7 @@
#include "usd_writer_mesh.h"
#include "usd_writer_metaball.h"
#include "usd_writer_transform.h"
+#include "usd_writer_volume.h"
#include <string>
@@ -28,10 +29,11 @@
namespace blender::io::usd {
-USDHierarchyIterator::USDHierarchyIterator(Depsgraph *depsgraph,
+USDHierarchyIterator::USDHierarchyIterator(Main *bmain,
+ Depsgraph *depsgraph,
pxr::UsdStageRefPtr stage,
const USDExportParams &params)
- : 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 &params_;
public:
- USDHierarchyIterator(Depsgraph *depsgraph,
+ USDHierarchyIterator(Main *bmain,
+ Depsgraph *depsgraph,
pxr::UsdStageRefPtr stage,
const USDExportParams &params);
void set_export_frame(float frame_nr);
+ std::string get_export_file_path() const;
const pxr::UsdTimeCode &get_export_time_code() const;
virtual std::string make_valid_name(const std::string &name) const override;
diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc
index dbeb9df1ee8..2be9b1c065a 100644
--- a/source/blender/io/usd/intern/usd_writer_abstract.cc
+++ b/source/blender/io/usd/intern/usd_writer_abstract.cc
@@ -47,6 +47,11 @@ bool USDAbstractWriter::is_supported(const HierarchyContext * /*context*/) const
return true;
}
+std::string USDAbstractWriter::get_export_file_path() const
+{
+ return usd_export_context_.hierarchy_iterator->get_export_file_path();
+}
+
pxr::UsdTimeCode USDAbstractWriter::get_export_time_code() const
{
if (is_animated_) {
diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h
index 66feb6e1070..8adc054c2c2 100644
--- a/source/blender/io/usd/intern/usd_writer_abstract.h
+++ b/source/blender/io/usd/intern/usd_writer_abstract.h
@@ -51,6 +51,7 @@ class USDAbstractWriter : public AbstractHierarchyWriter {
protected:
virtual void do_write(HierarchyContext &context) = 0;
+ std::string get_export_file_path() const;
pxr::UsdTimeCode get_export_time_code() const;
pxr::UsdShadeMaterial ensure_usd_material(const HierarchyContext &context, Material *material);
diff --git a/source/blender/io/usd/intern/usd_writer_material.cc b/source/blender/io/usd/intern/usd_writer_material.cc
index 1bfc0e50f69..a24877a20bd 100644
--- a/source/blender/io/usd/intern/usd_writer_material.cc
+++ b/source/blender/io/usd/intern/usd_writer_material.cc
@@ -576,7 +576,7 @@ static std::string get_tex_image_asset_path(bNode *node,
char file_path[FILE_MAX];
BLI_split_file_part(path.c_str(), file_path, FILE_MAX);
- if (export_params.relative_texture_paths) {
+ if (export_params.relative_paths) {
BLI_path_join(exp_path, FILE_MAX, ".", "textures", file_path, nullptr);
}
else {
@@ -594,7 +594,7 @@ static std::string get_tex_image_asset_path(bNode *node,
return exp_path;
}
- if (export_params.relative_texture_paths) {
+ if (export_params.relative_paths) {
/* Get the path relative to the USD. */
pxr::SdfLayerHandle layer = stage->GetRootLayer();
std::string stage_path = layer->GetRealPath();
@@ -606,11 +606,7 @@ static std::string get_tex_image_asset_path(bNode *node,
strcpy(rel_path, path.c_str());
BLI_path_rel(rel_path, stage_path.c_str());
-
- /* BLI_path_rel adds '//' as a prefix to the path, if
- * generating the relative path was successful. */
- if (rel_path[0] != '/' || rel_path[1] != '/') {
- /* No relative path generated. */
+ if (!BLI_path_is_rel(rel_path)) {
return path;
}
diff --git a/source/blender/io/usd/intern/usd_writer_volume.cc b/source/blender/io/usd/intern/usd_writer_volume.cc
new file mode 100644
index 00000000000..b83ded1e5d0
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_writer_volume.cc
@@ -0,0 +1,188 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "usd_writer_volume.h"
+
+#include <pxr/base/tf/pathUtils.h>
+#include <pxr/usd/usdVol/openVDBAsset.h>
+#include <pxr/usd/usdVol/volume.h>
+
+#include "DNA_volume_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "BKE_volume.h"
+
+#include "BLI_fileops.h"
+#include "BLI_index_range.hh"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+
+#include "WM_api.h"
+
+#include "usd_hierarchy_iterator.h"
+
+namespace blender::io::usd {
+
+USDVolumeWriter::USDVolumeWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx)
+{
+}
+
+bool USDVolumeWriter::check_is_animated(const HierarchyContext &context) const
+{
+ const Volume *volume = static_cast<Volume *>(context.object->data);
+ return volume->is_sequence;
+}
+
+void USDVolumeWriter::do_write(HierarchyContext &context)
+{
+ Volume *volume = static_cast<Volume *>(context.object->data);
+ if (!BKE_volume_load(volume, usd_export_context_.bmain)) {
+ return;
+ }
+
+ const int num_grids = BKE_volume_num_grids(volume);
+ if (!num_grids) {
+ return;
+ }
+
+ auto vdb_file_path = resolve_vdb_file(volume);
+ if (!vdb_file_path.has_value()) {
+ WM_reportf(RPT_WARNING,
+ "USD Export: failed to resolve .vdb file for object: %s",
+ volume->id.name + 2);
+ return;
+ }
+
+ if (usd_export_context_.export_params.relative_paths) {
+ if (auto relative_vdb_file_path = construct_vdb_relative_file_path(vdb_file_path.value())) {
+ vdb_file_path = relative_vdb_file_path;
+ }
+ else {
+ WM_reportf(RPT_WARNING,
+ "USD Export: couldn't construct relative file path for .vdb file, absolute path "
+ "will be used instead");
+ }
+ }
+
+ const pxr::UsdTimeCode timecode = get_export_time_code();
+ const pxr::SdfPath &volume_path = usd_export_context_.usd_path;
+ pxr::UsdStageRefPtr stage = usd_export_context_.stage;
+ pxr::UsdVolVolume usd_volume = pxr::UsdVolVolume::Define(stage, volume_path);
+
+ for (const int i : IndexRange(num_grids)) {
+ const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i);
+ const std::string grid_name = BKE_volume_grid_name(grid);
+ const std::string grid_id = pxr::TfMakeValidIdentifier(grid_name);
+ const pxr::SdfPath grid_path = volume_path.AppendPath(pxr::SdfPath(grid_id));
+ pxr::UsdVolOpenVDBAsset usd_grid = pxr::UsdVolOpenVDBAsset::Define(stage, grid_path);
+ usd_grid.GetFieldNameAttr().Set(pxr::TfToken(grid_name), timecode);
+ usd_grid.GetFilePathAttr().Set(pxr::SdfAssetPath(vdb_file_path.value()), timecode);
+ usd_volume.CreateFieldRelationship(pxr::TfToken(grid_id), grid_path);
+ }
+
+ float3 volume_bound_min(std::numeric_limits<float>::max());
+ float3 volume_bound_max(std::numeric_limits<float>::min());
+ if (BKE_volume_min_max(volume, volume_bound_min, volume_bound_max)) {
+ const pxr::VtArray<pxr::GfVec3f> volume_extent = {pxr::GfVec3f(&volume_bound_min[0]),
+ pxr::GfVec3f(&volume_bound_max[0])};
+ usd_volume.GetExtentAttr().Set(volume_extent, timecode);
+ }
+
+ BKE_volume_unload(volume);
+}
+
+std::optional<std::string> USDVolumeWriter::resolve_vdb_file(const Volume *volume) const
+{
+ std::optional<std::string> vdb_file_path;
+ if (volume->filepath[0] == '\0') {
+ /* Entering this section should mean that Volume object contains OpenVDB data that is not
+ * obtained from external .vdb file but rather generated inside of Blender (i.e. by 'Mesh to
+ * Volume' modifier). Try to save this data to a .vdb file. */
+
+ vdb_file_path = construct_vdb_file_path(volume);
+ if (!BKE_volume_save(
+ volume, usd_export_context_.bmain, NULL, vdb_file_path.value_or("").c_str())) {
+ return std::nullopt;
+ }
+ }
+
+ if (!vdb_file_path.has_value()) {
+ vdb_file_path = BKE_volume_grids_frame_filepath(volume);
+ if (vdb_file_path.value().empty()) {
+ return std::nullopt;
+ }
+ }
+
+ return vdb_file_path;
+}
+
+std::optional<std::string> USDVolumeWriter::construct_vdb_file_path(const Volume *volume) const
+{
+ const std::string usd_file_path = get_export_file_path();
+ if (usd_file_path.empty()) {
+ return std::nullopt;
+ }
+
+ char usd_directory_path[FILE_MAX];
+ char usd_file_name[FILE_MAXFILE];
+ BLI_split_dirfile(usd_file_path.c_str(),
+ usd_directory_path,
+ usd_file_name,
+ sizeof(usd_directory_path),
+ sizeof(usd_file_name));
+
+ if (usd_directory_path[0] == '\0' || usd_file_name[0] == '\0') {
+ return std::nullopt;
+ }
+
+ const char *vdb_directory_name = "volumes";
+
+ char vdb_directory_path[FILE_MAX];
+ BLI_strncpy(vdb_directory_path, usd_directory_path, FILE_MAX);
+ strcat(vdb_directory_path, vdb_directory_name);
+ BLI_dir_create_recursive(vdb_directory_path);
+
+ char vdb_file_name[FILE_MAXFILE];
+ BLI_strncpy(vdb_file_name, volume->id.name + 2, FILE_MAXFILE);
+ const pxr::UsdTimeCode timecode = get_export_time_code();
+ if (!timecode.IsDefault()) {
+ const int frame = (int)timecode.GetValue();
+ const int num_frame_digits = frame == 0 ? 1 : integer_digits_i(abs(frame));
+ BLI_path_frame(vdb_file_name, frame, num_frame_digits);
+ }
+ strcat(vdb_file_name, ".vdb");
+
+ char vdb_file_path[FILE_MAX];
+ BLI_path_join(vdb_file_path, sizeof(vdb_file_path), vdb_directory_path, vdb_file_name, NULL);
+
+ return vdb_file_path;
+}
+
+std::optional<std::string> USDVolumeWriter::construct_vdb_relative_file_path(
+ const std::string &vdb_file_path) const
+{
+ const std::string usd_file_path = get_export_file_path();
+ if (usd_file_path.empty()) {
+ return std::nullopt;
+ }
+
+ char relative_path[FILE_MAX];
+ BLI_strncpy(relative_path, vdb_file_path.c_str(), FILE_MAX);
+ BLI_path_rel(relative_path, usd_file_path.c_str());
+ if (!BLI_path_is_rel(relative_path)) {
+ return std::nullopt;
+ }
+
+ /* Following code was written with an assumption that Blender's relative paths start with
+ * // characters as well as have OS dependent slashes. Inside of USD files those relative
+ * paths should start with either ./ or ../ characters and have always forward slashes (/)
+ * separating directories. This is the convention used in USD documentation (and it seems
+ * to be used in other DCC packages as well). */
+ std::string relative_path_processed = pxr::TfNormPath(relative_path + 2);
+ if (relative_path_processed[0] != '.') {
+ relative_path_processed.insert(0, "./");
+ }
+
+ return relative_path_processed;
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_writer_volume.h b/source/blender/io/usd/intern/usd_writer_volume.h
new file mode 100644
index 00000000000..8c1e36b7e53
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_writer_volume.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#pragma once
+
+#include <optional>
+
+#include "BLI_math_vec_types.hh"
+#include "usd_writer_abstract.h"
+
+struct Volume;
+
+namespace blender::io::usd {
+
+/* Writer for writing OpenVDB assets to UsdVolVolume. Volume data is stored in separate .vdb files
+ * which are referenced in USD file. */
+class USDVolumeWriter : public USDAbstractWriter {
+ public:
+ USDVolumeWriter(const USDExporterContext &ctx);
+
+ protected:
+ virtual bool check_is_animated(const HierarchyContext &context) const override;
+ virtual void do_write(HierarchyContext &context) override;
+
+ private:
+ /* Try to ensure that external .vdb file is available for USD to be referenced. Blender can
+ * either reference external OpenVDB data or generate such data internally. Latter option will
+ * mean that `resolve_vdb_file` method will try to export volume data to a new .vdb file. If
+ * successful, this method returns absolute file path to the resolved .vdb file, if not, returns
+ * `std::nullopt`. */
+ std::optional<std::string> resolve_vdb_file(const Volume *volume) const;
+
+ std::optional<std::string> construct_vdb_file_path(const Volume *volume) const;
+ std::optional<std::string> construct_vdb_relative_file_path(
+ const std::string &vdb_file_path) const;
+};
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h
index e63cd0a4e04..deaa27a5252 100644
--- a/source/blender/io/usd/usd.h
+++ b/source/blender/io/usd/usd.h
@@ -28,7 +28,7 @@ struct USDExportParams {
bool generate_preview_surface;
bool export_textures;
bool overwrite_textures;
- bool relative_texture_paths;
+ bool relative_paths;
};
struct USDImportParams {
diff --git a/source/blender/io/wavefront_obj/CMakeLists.txt b/source/blender/io/wavefront_obj/CMakeLists.txt
index 67cec000778..f7958ef4ec6 100644
--- a/source/blender/io/wavefront_obj/CMakeLists.txt
+++ b/source/blender/io/wavefront_obj/CMakeLists.txt
@@ -15,6 +15,7 @@ set(INC
../../makesrna
../../nodes
../../windowmanager
+ ../../../../extern/fast_float
../../../../extern/fmtlib/include
../../../../intern/guardedalloc
)
@@ -35,6 +36,7 @@ set(SRC
importer/obj_import_mesh.cc
importer/obj_import_mtl.cc
importer/obj_import_nurbs.cc
+ importer/obj_import_string_utils.cc
importer/obj_importer.cc
IO_wavefront_obj.h
@@ -50,6 +52,7 @@ set(SRC
importer/obj_import_mtl.hh
importer/obj_import_nurbs.hh
importer/obj_import_objects.hh
+ importer/obj_import_string_utils.hh
importer/obj_importer.hh
)
@@ -69,8 +72,10 @@ blender_add_lib(bf_wavefront_obj "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
if(WITH_GTESTS)
set(TEST_SRC
tests/obj_exporter_tests.cc
+ tests/obj_import_string_utils_tests.cc
tests/obj_importer_tests.cc
tests/obj_mtl_parser_tests.cc
+
tests/obj_exporter_tests.hh
)
diff --git a/source/blender/io/wavefront_obj/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_mtl.cc b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc
index c2ecd8a37de..f39def0a4af 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc
@@ -13,13 +13,12 @@
#include "DNA_material_types.h"
#include "DNA_node_types.h"
-#include "IO_string_utils.hh"
-
#include "NOD_shader.h"
/* TODO: move eMTLSyntaxElement out of following file into a more neutral place */
#include "obj_export_io.hh"
#include "obj_import_mtl.hh"
+#include "obj_import_string_utils.hh"
namespace blender::io::obj {
diff --git a/source/blender/io/common/intern/string_utils.cc b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc
index 3a12250e14b..ffafbe41d0f 100644
--- a/source/blender/io/common/intern/string_utils.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "IO_string_utils.hh"
+#include "obj_import_string_utils.hh"
/* Note: we could use C++17 <charconv> from_chars to parse
* floats, but even if some compilers claim full support,
@@ -11,7 +11,7 @@
#include "fast_float.h"
#include <charconv>
-namespace blender::io {
+namespace blender::io::obj {
StringRef read_next_line(StringRef &buffer)
{
@@ -38,62 +38,63 @@ static bool is_whitespace(char c)
return c <= ' ' || c == '\\';
}
-StringRef drop_whitespace(StringRef str)
+const char *drop_whitespace(const char *p, const char *end)
{
- while (!str.is_empty() && is_whitespace(str[0])) {
- str = str.drop_prefix(1);
+ while (p < end && is_whitespace(*p)) {
+ ++p;
}
- return str;
+ return p;
}
-StringRef drop_non_whitespace(StringRef str)
+const char *drop_non_whitespace(const char *p, const char *end)
{
- while (!str.is_empty() && !is_whitespace(str[0])) {
- str = str.drop_prefix(1);
+ while (p < end && !is_whitespace(*p)) {
+ ++p;
}
- return str;
+ return p;
}
-static StringRef drop_plus(StringRef str)
+static const char *drop_plus(const char *p, const char *end)
{
- if (!str.is_empty() && str[0] == '+') {
- str = str.drop_prefix(1);
+ if (p < end && *p == '+') {
+ ++p;
}
- return str;
+ return p;
}
-StringRef parse_float(StringRef str, float fallback, float &dst, bool skip_space)
+const char *parse_float(
+ const char *p, const char *end, float fallback, float &dst, bool skip_space)
{
if (skip_space) {
- str = drop_whitespace(str);
+ p = drop_whitespace(p, end);
}
- str = drop_plus(str);
- fast_float::from_chars_result res = fast_float::from_chars(str.begin(), str.end(), dst);
+ 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 StringRef(res.ptr, str.end());
+ return res.ptr;
}
-StringRef parse_floats(StringRef str, float fallback, float *dst, int count)
+const char *parse_floats(const char *p, const char *end, float fallback, float *dst, int count)
{
for (int i = 0; i < count; ++i) {
- str = parse_float(str, fallback, dst[i]);
+ p = parse_float(p, end, fallback, dst[i]);
}
- return str;
+ return p;
}
-StringRef parse_int(StringRef str, int fallback, int &dst, bool skip_space)
+const char *parse_int(const char *p, const char *end, int fallback, int &dst, bool skip_space)
{
if (skip_space) {
- str = drop_whitespace(str);
+ p = drop_whitespace(p, end);
}
- str = drop_plus(str);
- std::from_chars_result res = std::from_chars(str.begin(), str.end(), dst);
+ 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 StringRef(res.ptr, str.end());
+ return res.ptr;
}
-} // namespace blender::io
+} // 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/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_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..39b75b6eda2 100644
--- a/source/blender/makesdna/DNA_lineart_types.h
+++ b/source/blender/makesdna/DNA_lineart_types.h
@@ -21,7 +21,7 @@ typedef enum eLineartMainFlags {
LRT_ALLOW_DUPLI_OBJECTS = (1 << 2),
LRT_ALLOW_OVERLAPPING_EDGES = (1 << 3),
LRT_ALLOW_CLIPPING_BOUNDARIES = (1 << 4),
- LRT_REMOVE_DOUBLES = (1 << 5),
+ /* LRT_REMOVE_DOUBLES = (1 << 5), Deprecated */
LRT_LOOSE_AS_CONTOUR = (1 << 6),
LRT_GPENCIL_INVERT_SOURCE_VGROUP = (1 << 7),
LRT_GPENCIL_MATCH_OUTPUT_VGROUP = (1 << 8),
@@ -47,9 +47,16 @@ typedef enum eLineartEdgeFlag {
LRT_EDGE_FLAG_MATERIAL = (1 << 3),
LRT_EDGE_FLAG_INTERSECTION = (1 << 4),
LRT_EDGE_FLAG_LOOSE = (1 << 5),
- LRT_EDGE_FLAG_CHAIN_PICKED = (1 << 6),
- LRT_EDGE_FLAG_CLIPPED = (1 << 7),
- /** Limited to 8 bits, DON'T ADD ANYMORE until improvements on the data structure. */
+ /* LRT_EDGE_FLAG_FOR_FUTURE = (1 << 7), */
+ /* Limited to 8 bits for edge type flag, don't add anymore because BMEdge->head.eflag only has 8
+ bits. So unless we changed this into a non-single-bit flag thing, we keep it this way. */
+ /** Also used as discarded line mark. */
+ LRT_EDGE_FLAG_CHAIN_PICKED = (1 << 8),
+ LRT_EDGE_FLAG_CLIPPED = (1 << 9),
+ /** Limited to 16 bits for the entire thing. */
+
+ /** For object loading code to use only. */
+ LRT_EDGE_FLAG_INHIBIT = (1 << 14),
} eLineartEdgeFlag;
#define LRT_EDGE_FLAG_ALL_TYPE 0x3f
diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h
index 77730ce254c..92a65a50bd4 100644
--- a/source/blender/makesdna/DNA_modifier_defaults.h
+++ b/source/blender/makesdna/DNA_modifier_defaults.h
@@ -632,7 +632,8 @@
.falloff = 4.0f, \
.mesh_verts_num = 0, \
.bind_verts_num = 0, \
- .polys_num = 0, \
+ .target_verts_num = 0, \
+ .target_polys_num = 0, \
.flags = 0, \
.mat = _DNA_DEFAULT_UNIT_M4, \
.strength = 1.0f, \
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index 73c4eeaaab3..6e3ce7e98a8 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -2222,13 +2222,19 @@ typedef struct SurfaceDeformModifierData {
struct Object *target;
/** Vertex bind data. */
SDefVert *verts;
+ void *_pad1;
float falloff;
- unsigned int mesh_verts_num, bind_verts_num, polys_num;
+ /* Number of of vertices on the deformed mesh upon the bind process. */
+ unsigned int mesh_verts_num;
+ /* Number of vertices in the `verts` array of this modifier. */
+ unsigned int bind_verts_num;
+ /* Number of vertices and polygons on the target mesh upon bind process. */
+ unsigned int target_verts_num, target_polys_num;
int flags;
float mat[4][4];
float strength;
char defgrp_name[64];
- void *_pad1;
+ int _pad2;
} SurfaceDeformModifierData;
/** Surface Deform modifier flags. */
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index d18fe1b81dd..92c4d8fe938 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -855,6 +855,12 @@ typedef struct NodeVertexCol {
char name[64];
} NodeVertexCol;
+typedef struct NodeCMPCombSepColor {
+ /* CMPNodeCombSepColorMode */
+ uint8_t mode;
+ uint8_t ycc_mode;
+} NodeCMPCombSepColor;
+
/** Defocus blur node. */
typedef struct NodeDefocus {
char bktype, _pad0, preview, gamco;
@@ -1485,6 +1491,11 @@ typedef struct NodeFunctionCompare {
char _pad[1];
} NodeFunctionCompare;
+typedef struct NodeCombSepColor {
+ /* NodeCombSepColorMode */
+ int8_t mode;
+} NodeCombSepColor;
+
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1
@@ -1877,6 +1888,16 @@ typedef enum CMPNodeDenoisePrefilter {
CMP_NODE_DENOISE_PREFILTER_ACCURATE = 2
} CMPNodeDenoisePrefilter;
+/* Color combine/separate modes */
+
+typedef enum CMPNodeCombSepColorMode {
+ CMP_NODE_COMBSEP_COLOR_RGB = 0,
+ CMP_NODE_COMBSEP_COLOR_HSV = 1,
+ CMP_NODE_COMBSEP_COLOR_HSL = 2,
+ CMP_NODE_COMBSEP_COLOR_YCC = 3,
+ CMP_NODE_COMBSEP_COLOR_YUV = 4,
+} CMPNodeCombSepColorMode;
+
#define CMP_NODE_PLANETRACKDEFORM_MBLUR_SAMPLES_MAX 64
/* Point Density shader node */
@@ -2135,6 +2156,12 @@ typedef enum GeometryNodeScaleElementsMode {
GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS = 1,
} GeometryNodeScaleElementsMode;
+typedef enum NodeCombSepColorMode {
+ NODE_COMBSEP_COLOR_RGB = 0,
+ NODE_COMBSEP_COLOR_HSV = 1,
+ NODE_COMBSEP_COLOR_HSL = 2,
+} NodeCombSepColorMode;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 9ea1e3a9e8d..f257833efe8 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -535,7 +535,8 @@ enum {
/** Matches #OB_TYPE_SUPPORT_EDITMODE. */
#define OB_DATA_SUPPORT_EDITMODE(_type) \
- (ELEM(_type, ID_ME, ID_CU_LEGACY, ID_MB, ID_LT, ID_AR, ID_CV))
+ (ELEM(_type, ID_ME, ID_CU_LEGACY, ID_MB, ID_LT, ID_AR) || \
+ (U.experimental.use_new_curves_tools && (_type) == ID_CV))
/* is this ID type used as object data */
#define OB_DATA_SUPPORT_ID(_id_type) \
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index c1e96bcfaf3..8e29d88a583 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -1223,7 +1223,8 @@ typedef struct SpaceImage {
char dt_uvstretch;
char around;
- char _pad1[4];
+ char gizmo_flag;
+ char _pad1[3];
int flag;
@@ -1321,6 +1322,13 @@ typedef enum eSpaceImageOverlay_Flag {
SI_OVERLAY_SHOW_GRID_BACKGROUND = (1 << 1),
} eSpaceImageOverlay_Flag;
+/** #SpaceImage.gizmo_flag */
+enum {
+ /** All gizmos. */
+ SI_GIZMO_HIDE = (1 << 0),
+ SI_GIZMO_HIDE_NAVIGATE = (1 << 1),
+};
+
/** Keep in sync with `STEPS_LEN` in `grid_frag.glsl`. */
#define SI_GRID_STEPS_LEN 8
@@ -1332,7 +1340,7 @@ typedef enum eSpaceImageOverlay_Flag {
typedef struct SpaceText_Runtime {
- /** Actual line height, scaled by dpi. */
+ /** Actual line height, scaled by DPI. */
int lheight_px;
/** Runtime computed, character width. */
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 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/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h
index 74f8e2487a4..127c8465243 100644
--- a/source/blender/makesrna/RNA_enum_items.h
+++ b/source/blender/makesrna/RNA_enum_items.h
@@ -198,8 +198,10 @@ DEF_ENUM(rna_enum_context_mode_items)
DEF_ENUM(rna_enum_preference_section_items)
DEF_ENUM(rna_enum_attribute_type_items)
+DEF_ENUM(rna_enum_color_attribute_type_items)
DEF_ENUM(rna_enum_attribute_type_with_auto_items)
DEF_ENUM(rna_enum_attribute_domain_items)
+DEF_ENUM(rna_enum_color_attribute_domain_items)
DEF_ENUM(rna_enum_attribute_domain_without_corner_items)
DEF_ENUM(rna_enum_attribute_domain_with_auto_items)
DEF_ENUM(rna_enum_geometry_component_type_items)
diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c
index d49bac15242..2d9517ce1ee 100644
--- a/source/blender/makesrna/intern/rna_attribute.c
+++ b/source/blender/makesrna/intern/rna_attribute.c
@@ -40,12 +40,21 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = {
{0, NULL, 0, NULL, NULL},
};
+const EnumPropertyItem rna_enum_color_attribute_type_items[] = {
+ {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color 32-bit floating-point values"},
+ {CD_PROP_BYTE_COLOR,
+ "BYTE_COLOR",
+ 0,
+ "Byte Color",
+ "RGBA color with 8-bit positive integer values"},
+ {0, NULL, 0, NULL, NULL}};
+
const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = {
{CD_AUTO_FROM_NAME, "AUTO", 0, "Auto", ""},
{CD_PROP_FLOAT, "FLOAT", 0, "Float", "Floating-point value"},
{CD_PROP_INT32, "INT", 0, "Integer", "32-bit integer"},
{CD_PROP_FLOAT3, "FLOAT_VECTOR", 0, "Vector", "3D vector with floating-point values"},
- {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color 32-bit floating-point values"},
+ {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color with 32-bit floating-point values"},
{CD_PROP_BYTE_COLOR,
"BYTE_COLOR",
0,
@@ -92,6 +101,11 @@ const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[] = {
{0, NULL, 0, NULL, NULL},
};
+const EnumPropertyItem rna_enum_color_attribute_domain_items[] = {
+ {ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", ""},
+ {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""},
+ {0, NULL, 0, NULL, NULL}};
+
#ifdef RNA_RUNTIME
# include "BLI_math.h"
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index 848779c49f7..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..60530eb8bf9 100644
--- a/source/blender/makesrna/intern/rna_curves.c
+++ b/source/blender/makesrna/intern/rna_curves.c
@@ -38,7 +38,7 @@ static Curves *rna_curves(PointerRNA *ptr)
static int rna_Curves_curve_offset_data_length(PointerRNA *ptr)
{
const Curves *curves = rna_curves(ptr);
- return curves->geometry.curve_size + 1;
+ return curves->geometry.curve_num + 1;
}
static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
@@ -47,7 +47,7 @@ static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter,
rna_iterator_array_begin(iter,
(void *)curves->geometry.curve_offsets,
sizeof(int),
- curves->geometry.curve_size + 1,
+ curves->geometry.curve_num + 1,
false,
NULL);
}
@@ -222,7 +222,7 @@ static void rna_def_curves(BlenderRNA *brna)
/* Point and Curve RNA API helpers. */
prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", "geometry.curve_size");
+ RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", "geometry.curve_num");
RNA_def_property_struct_type(prop, "CurveSlice");
RNA_def_property_ui_text(prop, "Curves", "All curves in the data-block");
@@ -230,7 +230,7 @@ static void rna_def_curves(BlenderRNA *brna)
RNA_define_verify_sdna(0);
prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_size");
+ RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_num");
RNA_def_property_struct_type(prop, "CurvePoint");
RNA_def_property_ui_text(prop, "Points", "Control points of all curves");
RNA_define_verify_sdna(1);
@@ -239,7 +239,7 @@ static void rna_def_curves(BlenderRNA *brna)
RNA_define_verify_sdna(0);
prop = RNA_def_property(srna, "position_data", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_size");
+ RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_num");
RNA_def_property_struct_type(prop, "FloatVectorAttributeValue");
RNA_def_property_update(prop, 0, "rna_Curves_update_data");
RNA_define_verify_sdna(1);
@@ -274,6 +274,22 @@ static void rna_def_curves(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Surface", "Mesh object that the curves can be attached to");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+ /* Symmetry. */
+ prop = RNA_def_property(srna, "use_mirror_x", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "symmetry", CURVES_SYMMETRY_X);
+ RNA_def_property_ui_text(prop, "X", "Enable symmetry in the X axis");
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
+
+ prop = RNA_def_property(srna, "use_mirror_y", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "symmetry", CURVES_SYMMETRY_Y);
+ RNA_def_property_ui_text(prop, "Y", "Enable symmetry in the Y axis");
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
+
+ prop = RNA_def_property(srna, "use_mirror_z", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "symmetry", CURVES_SYMMETRY_Z);
+ RNA_def_property_ui_text(prop, "Z", "Enable symmetry in the Z axis");
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
+
/* attributes */
rna_def_attributes_common(srna);
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index 1f9bb972923..05e8d5406b4 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 interval 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..7bd2040dab0 100644
--- a/source/blender/makesrna/intern/rna_image_api.c
+++ b/source/blender/makesrna/intern/rna_image_api.c
@@ -161,9 +161,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_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index 456f774648a..6dd7fe53774 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -130,7 +130,7 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
ICON_MOD_EDGESPLIT,
"Edge Split",
"Split away joined faces at the edges"},
- {eModifierType_Nodes, "NODES", ICON_NODETREE, "Geometry Nodes", ""},
+ {eModifierType_Nodes, "NODES", ICON_GEOMETRY_NODES, "Geometry Nodes", ""},
{eModifierType_Mask,
"MASK",
ICON_MOD_MASK,
@@ -6991,7 +6991,7 @@ static void rna_def_modifier_nodes(BlenderRNA *brna)
RNA_def_struct_ui_text(srna, "Nodes Modifier", "");
RNA_def_struct_sdna(srna, "NodesModifierData");
RNA_def_struct_idprops_func(srna, "rna_NodesModifier_properties");
- RNA_def_struct_ui_icon(srna, ICON_NODETREE);
+ RNA_def_struct_ui_icon(srna, ICON_GEOMETRY_NODES);
RNA_define_lib_overridable(true);
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index eaf2142495e..9b9afadbd05 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -489,6 +489,13 @@ static const EnumPropertyItem rna_node_geometry_curve_handle_side_items[] = {
{GEO_NODE_CURVE_HANDLE_RIGHT, "RIGHT", ICON_NONE, "Right", "Use the right handles"},
{0, NULL, 0, NULL, NULL}};
+static const EnumPropertyItem rna_node_combsep_color_items[] = {
+ {NODE_COMBSEP_COLOR_RGB, "RGB", ICON_NONE, "RGB", "Use RGB color processing"},
+ {NODE_COMBSEP_COLOR_HSV, "HSV", ICON_NONE, "HSV", "Use HSV color processing"},
+ {NODE_COMBSEP_COLOR_HSL, "HSL", ICON_NONE, "HSL", "Use HSL color processing"},
+ {0, NULL, 0, NULL, NULL},
+};
+
#ifndef RNA_RUNTIME
static const EnumPropertyItem node_sampler_type_items[] = {
{0, "NEAREST", 0, "Nearest", ""},
@@ -5050,6 +5057,18 @@ static void def_fn_input_string(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_fn_combsep_color(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeCombSepColor", "storage");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_combsep_color_items);
+ RNA_def_property_ui_text(prop, "Mode", "Mode of color processing");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
/* -- Shader Nodes ---------------------------------------------------------- */
static void def_sh_output(StructRNA *srna)
@@ -5401,6 +5420,17 @@ static void def_sh_tex_image(StructRNA *srna)
RNA_def_property_update(prop, 0, "rna_Node_update");
}
+static void def_tex_combsep_color(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, rna_node_combsep_color_items);
+ RNA_def_property_ui_text(prop, "Mode", "Mode of color processing");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_geo_image_texture(StructRNA *srna)
{
static const EnumPropertyItem fn_tex_prop_interpolation_items[] = {
@@ -6308,6 +6338,25 @@ static void def_sh_output_aov(StructRNA *srna)
RNA_def_struct_sdna_from(srna, "bNode", NULL);
}
+static void def_sh_combsep_color(StructRNA *srna)
+{
+ static const EnumPropertyItem type_items[] = {
+ {NODE_COMBSEP_COLOR_RGB, "RGB", ICON_NONE, "RGB", "Use RGB color processing"},
+ {NODE_COMBSEP_COLOR_HSV, "HSV", ICON_NONE, "HSV", "Use HSV color processing"},
+ {NODE_COMBSEP_COLOR_HSL, "HSL", ICON_NONE, "HSL", "Use HSL color processing"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeCombSepColor", "storage");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, type_items);
+ RNA_def_property_ui_text(prop, "Mode", "Mode of color processing");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_sh_script(StructRNA *srna)
{
PropertyRNA *prop;
@@ -8071,6 +8120,32 @@ static void def_cmp_ycc(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_cmp_combsep_color(StructRNA *srna)
+{
+ static const EnumPropertyItem mode_items[] = {
+ {CMP_NODE_COMBSEP_COLOR_RGB, "RGB", ICON_NONE, "RGB", "Use RGB color processing"},
+ {CMP_NODE_COMBSEP_COLOR_HSV, "HSV", ICON_NONE, "HSV", "Use HSV color processing"},
+ {CMP_NODE_COMBSEP_COLOR_HSL, "HSL", ICON_NONE, "HSL", "Use HSL color processing"},
+ {CMP_NODE_COMBSEP_COLOR_YCC, "YCC", ICON_NONE, "YCbCr", "Use YCbCr color processing"},
+ {CMP_NODE_COMBSEP_COLOR_YUV, "YUV", ICON_NONE, "YUV", "Use YUV color processing"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeCMPCombSepColor", "storage");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, mode_items);
+ RNA_def_property_ui_text(prop, "Mode", "Mode of color processing");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "ycc_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, node_ycc_items);
+ RNA_def_property_ui_text(prop, "Color Space", "Color space used for YCbCrA processing");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
static void def_cmp_movieclip(StructRNA *srna)
{
PropertyRNA *prop;
@@ -12294,7 +12369,7 @@ static void rna_def_nodetree(BlenderRNA *brna)
{NTREE_SHADER, "SHADER", ICON_MATERIAL, "Shader", "Shader nodes"},
{NTREE_TEXTURE, "TEXTURE", ICON_TEXTURE, "Texture", "Texture nodes"},
{NTREE_COMPOSIT, "COMPOSITING", ICON_RENDERLAYERS, "Compositing", "Compositing nodes"},
- {NTREE_GEOMETRY, "GEOMETRY", ICON_NODETREE, "Geometry", "Geometry nodes"},
+ {NTREE_GEOMETRY, "GEOMETRY", ICON_GEOMETRY_NODES, "Geometry", "Geometry nodes"},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c
index f92ea8df459..addc8ac0c6c 100644
--- a/source/blender/makesrna/intern/rna_object_force.c
+++ b/source/blender/makesrna/intern/rna_object_force.c
@@ -1395,97 +1395,97 @@ static void rna_def_field(BlenderRNA *brna)
PropertyRNA *prop;
static const EnumPropertyItem field_type_items[] = {
- {0, "NONE", 0, "None", ""},
+ {0, "NONE", ICON_BLANK1, "None", ""},
+ {PFIELD_BOID,
+ "BOID",
+ ICON_FORCE_BOID,
+ "Boid",
+ "Create a force that acts as a boid's predators or target"},
+ {PFIELD_CHARGE,
+ "CHARGE",
+ ICON_FORCE_CHARGE,
+ "Charge",
+ "Spherical forcefield based on the charge of particles, "
+ "only influences other charge force fields"},
+ {PFIELD_GUIDE,
+ "GUIDE",
+ ICON_FORCE_CURVE,
+ "Curve Guide",
+ "Create a force along a curve object"},
+ {PFIELD_DRAG, "DRAG", ICON_FORCE_DRAG, "Drag", "Create a force that dampens motion"},
+ {PFIELD_FLUIDFLOW,
+ "FLUID_FLOW",
+ ICON_FORCE_FLUIDFLOW,
+ "Fluid Flow",
+ "Create a force based on fluid simulation velocities"},
{PFIELD_FORCE,
"FORCE",
ICON_FORCE_FORCE,
"Force",
"Radial field toward the center of object"},
- {PFIELD_WIND,
- "WIND",
- ICON_FORCE_WIND,
- "Wind",
- "Constant force along the force object's local Z axis"},
- {PFIELD_VORTEX,
- "VORTEX",
- ICON_FORCE_VORTEX,
- "Vortex",
- "Spiraling force that twists the force object's local Z axis"},
- {PFIELD_MAGNET,
- "MAGNET",
- ICON_FORCE_MAGNETIC,
- "Magnetic",
- "Forcefield depends on the speed of the particles"},
{PFIELD_HARMONIC,
"HARMONIC",
ICON_FORCE_HARMONIC,
"Harmonic",
"The source of this force field is the zero point of a harmonic oscillator"},
- {PFIELD_CHARGE,
- "CHARGE",
- ICON_FORCE_CHARGE,
- "Charge",
- "Spherical forcefield based on the charge of particles, "
- "only influences other charge force fields"},
{PFIELD_LENNARDJ,
"LENNARDJ",
ICON_FORCE_LENNARDJONES,
"Lennard-Jones",
"Forcefield based on the Lennard-Jones potential"},
+ {PFIELD_MAGNET,
+ "MAGNET",
+ ICON_FORCE_MAGNETIC,
+ "Magnetic",
+ "Forcefield depends on the speed of the particles"},
{PFIELD_TEXTURE, "TEXTURE", ICON_FORCE_TEXTURE, "Texture", "Force field based on a texture"},
- {PFIELD_GUIDE,
- "GUIDE",
- ICON_FORCE_CURVE,
- "Curve Guide",
- "Create a force along a curve object"},
- {PFIELD_BOID,
- "BOID",
- ICON_FORCE_BOID,
- "Boid",
- "Create a force that acts as a boid's predators or target"},
{PFIELD_TURBULENCE,
"TURBULENCE",
ICON_FORCE_TURBULENCE,
"Turbulence",
"Create turbulence with a noise field"},
- {PFIELD_DRAG, "DRAG", ICON_FORCE_DRAG, "Drag", "Create a force that dampens motion"},
- {PFIELD_FLUIDFLOW,
- "FLUID_FLOW",
- ICON_FORCE_FLUIDFLOW,
- "Fluid Flow",
- "Create a force based on fluid simulation velocities"},
+ {PFIELD_VORTEX,
+ "VORTEX",
+ ICON_FORCE_VORTEX,
+ "Vortex",
+ "Spiraling force that twists the force object's local Z axis"},
+ {PFIELD_WIND,
+ "WIND",
+ ICON_FORCE_WIND,
+ "Wind",
+ "Constant force along the force object's local Z axis"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem falloff_items[] = {
+ {PFIELD_FALL_CONE, "CONE", 0, "Cone", ""},
{PFIELD_FALL_SPHERE, "SPHERE", 0, "Sphere", ""},
{PFIELD_FALL_TUBE, "TUBE", 0, "Tube", ""},
- {PFIELD_FALL_CONE, "CONE", 0, "Cone", ""},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem texture_items[] = {
- {PFIELD_TEX_RGB, "RGB", 0, "RGB", ""},
- {PFIELD_TEX_GRAD, "GRADIENT", 0, "Gradient", ""},
{PFIELD_TEX_CURL, "CURL", 0, "Curl", ""},
+ {PFIELD_TEX_GRAD, "GRADIENT", 0, "Gradient", ""},
+ {PFIELD_TEX_RGB, "RGB", 0, "RGB", ""},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem zdirection_items[] = {
- {PFIELD_Z_BOTH, "BOTH", 0, "Both Z", ""},
{PFIELD_Z_POS, "POSITIVE", 0, "+Z", ""},
{PFIELD_Z_NEG, "NEGATIVE", 0, "-Z", ""},
+ {PFIELD_Z_BOTH, "BOTH", 0, "Both Z", ""},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem guide_kink_items[] = {
- {0, "NONE", 0, "Nothing", ""},
+ {0, "NONE", 0, "None", ""},
+ {4, "BRAID", 0, "Braid", ""},
{1, "CURL", 0, "Curl", ""},
{2, "RADIAL", 0, "Radial", ""},
- {3, "WAVE", 0, "Wave", ""},
- {4, "BRAID", 0, "Braid", ""},
- {5, "ROTATION", 0, "Rotation", ""},
{6, "ROLL", 0, "Roll", ""},
+ {5, "ROTATION", 0, "Rotation", ""},
+ {3, "WAVE", 0, "Wave", ""},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 1a9e7f5aad8..dda467b06b8 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -3292,7 +3292,7 @@ static struct IDFilterEnumPropertyItem rna_enum_space_file_id_filter_categories[
{FILTER_ID_AR | FILTER_ID_CU_LEGACY | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME |
FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO,
"category_geometry",
- ICON_NODETREE,
+ ICON_GEOMETRY_NODES,
"Geometry",
"Show meshes, curves, lattice, armatures and metaballs data"},
{FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE,
@@ -5407,6 +5407,17 @@ static void rna_def_space_image(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Show Mask Editor", "Show Mask editing related properties");
+ /* Gizmo Toggles. */
+ prop = RNA_def_property(srna, "show_gizmo", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "gizmo_flag", SI_GIZMO_HIDE);
+ RNA_def_property_ui_text(prop, "Show Gizmo", "Show gizmos of all types");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
+
+ prop = RNA_def_property(srna, "show_gizmo_navigate", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "gizmo_flag", SI_GIZMO_HIDE_NAVIGATE);
+ RNA_def_property_ui_text(prop, "Navigate Gizmo", "Viewport navigation gizmo");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
+
/* Overlays */
prop = RNA_def_property(srna, "overlay", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
diff --git a/source/blender/makesrna/intern/rna_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 053353b41ba..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");
@@ -6410,7 +6426,8 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_draw_manager_acquire_lock", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_draw_manager_acquire_lock", 1);
- RNA_def_property_ui_text(prop, "Draw Manager Locking", "Don't lock UI during background rendering");
+ RNA_def_property_ui_text(
+ prop, "Draw Manager Locking", "Don't lock UI during background rendering");
prop = RNA_def_property(srna, "use_extended_asset_browser", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_ui_text(prop,
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index b09a9ab0733..0ac6dc7aaa9 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -896,8 +896,10 @@ static void rna_wmKeyMapItem_map_type_set(PointerRNA *ptr, int value)
}
}
-/* assumes value to be an enum from rna_enum_event_type_items */
-/* function makes sure keymodifiers are only valid keys, ESC keeps it unaltered */
+/**
+ * Assumes value to be an enum from rna_enum_event_type_items.
+ * Function makes sure keymodifiers are only valid keys, ESC keeps it unaltered.
+ */
static void rna_wmKeyMapItem_keymodifier_set(PointerRNA *ptr, int value)
{
wmKeyMapItem *kmi = ptr->data;
diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c
index a04b29b8815..696d2d0f31d 100644
--- a/source/blender/makesrna/intern/rna_xr.c
+++ b/source/blender/makesrna/intern/rna_xr.c
@@ -1196,6 +1196,50 @@ static int rna_XrEventData_action_length(PointerRNA *ptr)
# endif
}
+static void rna_XrEventData_user_path_get(PointerRNA *ptr, char *r_value)
+{
+# ifdef WITH_XR_OPENXR
+ const wmXrActionData *data = ptr->data;
+ strcpy(r_value, data->user_path);
+# else
+ UNUSED_VARS(ptr);
+ r_value[0] = '\0';
+# endif
+}
+
+static int rna_XrEventData_user_path_length(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ const wmXrActionData *data = ptr->data;
+ return strlen(data->user_path);
+# else
+ UNUSED_VARS(ptr);
+ return 0;
+# endif
+}
+
+static void rna_XrEventData_user_path_other_get(PointerRNA *ptr, char *r_value)
+{
+# ifdef WITH_XR_OPENXR
+ const wmXrActionData *data = ptr->data;
+ strcpy(r_value, data->user_path_other);
+# else
+ UNUSED_VARS(ptr);
+ r_value[0] = '\0';
+# endif
+}
+
+static int rna_XrEventData_user_path_other_length(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ const wmXrActionData *data = ptr->data;
+ return strlen(data->user_path_other);
+# else
+ UNUSED_VARS(ptr);
+ return 0;
+# endif
+}
+
static int rna_XrEventData_type_get(PointerRNA *ptr)
{
# ifdef WITH_XR_OPENXR
@@ -2402,6 +2446,19 @@ static void rna_def_xr_eventdata(BlenderRNA *brna)
prop, "rna_XrEventData_action_get", "rna_XrEventData_action_length", NULL);
RNA_def_property_ui_text(prop, "Action", "XR action name");
+ prop = RNA_def_property(srna, "user_path", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_string_funcs(
+ prop, "rna_XrEventData_user_path_get", "rna_XrEventData_user_path_length", NULL);
+ RNA_def_property_ui_text(prop, "User Path", "User path of the action. E.g. \"/user/hand/left\"");
+
+ prop = RNA_def_property(srna, "user_path_other", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_string_funcs(
+ prop, "rna_XrEventData_user_path_other_get", "rna_XrEventData_user_path_other_length", NULL);
+ RNA_def_property_ui_text(
+ prop, "User Path Other", "Other user path, for bimanual actions. E.g. \"/user/hand/right\"");
+
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_enum_items(prop, rna_enum_xr_action_types);
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index a5e5bf36dcd..1aac3c2191d 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -70,7 +70,7 @@ set(SRC
intern/MOD_normal_edit.c
intern/MOD_ocean.c
intern/MOD_particleinstance.c
- intern/MOD_particlesystem.c
+ intern/MOD_particlesystem.cc
intern/MOD_remesh.c
intern/MOD_screw.c
intern/MOD_shapekey.c
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index 21041e8e1b2..cdf16d813f3 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -985,17 +985,16 @@ static Vector<OutputAttributeToStore> compute_attributes_to_store(
if (!component.attribute_domain_supported(domain)) {
continue;
}
- const int domain_size = component.attribute_domain_size(domain);
+ const int domain_num = component.attribute_domain_num(domain);
blender::bke::GeometryComponentFieldContext field_context{component, domain};
- blender::fn::FieldEvaluator field_evaluator{field_context, domain_size};
+ blender::fn::FieldEvaluator field_evaluator{field_context, domain_num};
for (const OutputAttributeInfo &output_info : outputs_info) {
const CPPType &type = output_info.field.cpp_type();
OutputAttributeToStore store{
component_type,
domain,
output_info.name,
- GMutableSpan{
- type, MEM_malloc_arrayN(domain_size, type.size(), __func__), domain_size}};
+ GMutableSpan{type, MEM_malloc_arrayN(domain_num, type.size(), __func__), domain_num}};
field_evaluator.add_with_destination(output_info.field, store.data);
attributes_to_store.append(store);
}
@@ -1799,7 +1798,7 @@ ModifierTypeInfo modifierType_Nodes = {
eModifierTypeFlag_SupportsEditmode |
eModifierTypeFlag_EnableInEditmode |
eModifierTypeFlag_SupportsMapping),
- /* icon */ ICON_NODETREE,
+ /* icon */ ICON_GEOMETRY_NODES,
/* copyData */ copyData,
diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.cc
index 032227307e7..0f75038189a 100644
--- a/source/blender/modifiers/intern/MOD_particlesystem.c
+++ b/source/blender/modifiers/intern/MOD_particlesystem.cc
@@ -51,11 +51,11 @@ static void freeData(ModifierData *md)
ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
if (psmd->mesh_final) {
- BKE_id_free(NULL, psmd->mesh_final);
- psmd->mesh_final = NULL;
+ BKE_id_free(nullptr, psmd->mesh_final);
+ psmd->mesh_final = nullptr;
if (psmd->mesh_original) {
- BKE_id_free(NULL, psmd->mesh_original);
- psmd->mesh_original = NULL;
+ BKE_id_free(nullptr, psmd->mesh_original);
+ psmd->mesh_original = nullptr;
}
}
psmd->totdmvert = psmd->totdmedge = psmd->totdmface = 0;
@@ -81,8 +81,8 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
* code has to be called then to ensure proper remapping of that pointer. See e.g.
* `BKE_object_copy_particlesystems` or `BKE_object_copy_modifier`. */
- tpsmd->mesh_final = NULL;
- tpsmd->mesh_original = NULL;
+ tpsmd->mesh_final = nullptr;
+ tpsmd->mesh_original = nullptr;
tpsmd->totdmvert = tpsmd->totdmedge = tpsmd->totdmface = 0;
}
@@ -104,7 +104,7 @@ static void deformVerts(ModifierData *md,
{
Mesh *mesh_src = mesh;
ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
- ParticleSystem *psys = NULL;
+ ParticleSystem *psys = nullptr;
if (ctx->object->particlesystem.first) {
psys = psmd->psys;
@@ -117,28 +117,28 @@ static void deformVerts(ModifierData *md,
return;
}
- if (mesh_src == NULL) {
+ if (mesh_src == nullptr) {
mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, NULL, vertexCos, verts_num, false, true);
- if (mesh_src == NULL) {
+ ctx->object, nullptr, nullptr, vertexCos, verts_num, false, true);
+ if (mesh_src == nullptr) {
return;
}
}
/* clear old dm */
- bool had_mesh_final = (psmd->mesh_final != NULL);
+ bool had_mesh_final = (psmd->mesh_final != nullptr);
if (psmd->mesh_final) {
- BKE_id_free(NULL, psmd->mesh_final);
- psmd->mesh_final = NULL;
+ BKE_id_free(nullptr, psmd->mesh_final);
+ psmd->mesh_final = nullptr;
if (psmd->mesh_original) {
- BKE_id_free(NULL, psmd->mesh_original);
- psmd->mesh_original = NULL;
+ BKE_id_free(nullptr, psmd->mesh_original);
+ psmd->mesh_original = nullptr;
}
}
else if (psmd->flag & eParticleSystemFlag_file_loaded) {
/* in file read mesh just wasn't saved in file so no need to reset everything */
psmd->flag &= ~eParticleSystemFlag_file_loaded;
- if (psys->particles == NULL) {
+ if (psys->particles == nullptr) {
psys->recalc |= ID_RECALC_PSYS_RESET;
}
/* TODO(sergey): This is not how particles were working prior to copy on
@@ -165,18 +165,18 @@ static void deformVerts(ModifierData *md,
/* Get the original mesh from the object, this is what the particles
* are attached to so in case of non-deform modifiers we need to remap
* them to the final mesh (typically subdivision surfaces). */
- Mesh *mesh_original = NULL;
+ Mesh *mesh_original = nullptr;
if (ctx->object->type == OB_MESH) {
BMEditMesh *em = BKE_editmesh_from_object(ctx->object);
if (em) {
/* In edit mode get directly from the edit mesh. */
- psmd->mesh_original = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, NULL, mesh);
+ psmd->mesh_original = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, nullptr, mesh);
}
else {
/* Otherwise get regular mesh. */
- mesh_original = ctx->object->data;
+ mesh_original = static_cast<Mesh *>(ctx->object->data);
}
}
else {
@@ -193,8 +193,8 @@ static void deformVerts(ModifierData *md,
BKE_mesh_tessface_ensure(psmd->mesh_original);
}
- if (!ELEM(mesh_src, NULL, mesh, psmd->mesh_final)) {
- BKE_id_free(NULL, mesh_src);
+ if (!ELEM(mesh_src, nullptr, mesh, psmd->mesh_final)) {
+ BKE_id_free(nullptr, mesh_src);
}
/* Report change in mesh structure.
@@ -221,7 +221,7 @@ static void deformVerts(ModifierData *md,
if (DEG_is_active(ctx->depsgraph)) {
Object *object_orig = DEG_get_original_object(ctx->object);
ModifierData *md_orig = BKE_modifiers_findby_name(object_orig, psmd->modifier.name);
- BLI_assert(md_orig != NULL);
+ BLI_assert(md_orig != nullptr);
ParticleSystemModifierData *psmd_orig = (ParticleSystemModifierData *)md_orig;
psmd_orig->flag = psmd->flag;
}
@@ -237,16 +237,16 @@ static void deformVertsEM(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- const bool do_temp_mesh = (mesh == NULL);
+ const bool do_temp_mesh = (mesh == nullptr);
if (do_temp_mesh) {
mesh = BKE_id_new_nomain(ID_ME, ((ID *)ob->data)->name);
- BM_mesh_bm_to_me(NULL, editData->bm, mesh, &((BMeshToMeshParams){0}));
+ BM_mesh_bm_to_me(nullptr, editData->bm, mesh, &((BMeshToMeshParams){0}));
}
deformVerts(md, ob, mesh, vertexCos, verts_num);
if (derivedData) {
- BKE_id_free(NULL, mesh);
+ BKE_id_free(nullptr, mesh);
}
}
#endif
@@ -258,7 +258,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
PointerRNA ob_ptr;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
- Object *ob = ob_ptr.data;
+ Object *ob = static_cast<Object *>(ob_ptr.data);
ModifierData *md = (ModifierData *)ptr->data;
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
@@ -291,8 +291,8 @@ static void blendRead(BlendDataReader *reader, ModifierData *md)
{
ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
- psmd->mesh_final = NULL;
- psmd->mesh_original = NULL;
+ psmd->mesh_final = nullptr;
+ psmd->mesh_original = nullptr;
/* This is written as part of ob->particlesystem. */
BLO_read_data_address(reader, &psmd->psys);
psmd->flag &= ~eParticleSystemFlag_psys_updated;
@@ -315,23 +315,23 @@ ModifierTypeInfo modifierType_ParticleSystem = {
/* copyData */ copyData,
/* deformVerts */ deformVerts,
- /* deformMatrices */ NULL,
- /* deformVertsEM */ NULL,
- /* deformMatricesEM */ NULL,
- /* modifyMesh */ NULL,
- /* modifyGeometrySet */ NULL,
+ /* deformMatrices */ nullptr,
+ /* deformVertsEM */ nullptr,
+ /* deformMatricesEM */ nullptr,
+ /* modifyMesh */ nullptr,
+ /* modifyGeometrySet */ nullptr,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
/* freeData */ freeData,
- /* isDisabled */ NULL,
- /* updateDepsgraph */ NULL,
- /* dependsOnTime */ NULL,
- /* dependsOnNormals */ NULL,
- /* foreachIDLink */ NULL,
- /* foreachTexLink */ NULL,
- /* freeRuntimeData */ NULL,
+ /* isDisabled */ nullptr,
+ /* updateDepsgraph */ nullptr,
+ /* dependsOnTime */ nullptr,
+ /* dependsOnNormals */ nullptr,
+ /* foreachIDLink */ nullptr,
+ /* foreachTexLink */ nullptr,
+ /* freeRuntimeData */ nullptr,
/* panelRegister */ panelRegister,
- /* blendWrite */ NULL,
+ /* blendWrite */ nullptr,
/* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c
index 08925e8aeb1..55fca7e8ecb 100644
--- a/source/blender/modifiers/intern/MOD_screw.c
+++ b/source/blender/modifiers/intern/MOD_screw.c
@@ -375,6 +375,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 +389,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;
@@ -472,7 +477,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 +775,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 +856,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 +881,12 @@ 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 */
+ float nor_tx[3];
+ mul_v3_m3v3(nor_tx, mat3, vert_connect[j].no);
+ copy_v3_v3(vert_normals_new[mv_new - mvert_new], nor_tx);
+ }
}
/* set location */
@@ -1118,11 +1129,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_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c
index a80918b8d2b..8a0f49efb65 100644
--- a/source/blender/modifiers/intern/MOD_surfacedeform.c
+++ b/source/blender/modifiers/intern/MOD_surfacedeform.c
@@ -1166,8 +1166,8 @@ static bool surfacedeformBind(Object *ob,
SurfaceDeformModifierData *smd_eval,
float (*vertexCos)[3],
uint verts_num,
- uint tpolys_num,
- uint tverts_num,
+ uint target_polys_num,
+ uint target_verts_num,
Mesh *target,
Mesh *mesh)
{
@@ -1182,7 +1182,7 @@ static bool surfacedeformBind(Object *ob,
SDefAdjacency *adj_array;
SDefEdgePolys *edge_polys;
- vert_edges = MEM_calloc_arrayN(tverts_num, sizeof(*vert_edges), "SDefVertEdgeMap");
+ vert_edges = MEM_calloc_arrayN(target_verts_num, sizeof(*vert_edges), "SDefVertEdgeMap");
if (vert_edges == NULL) {
BKE_modifier_set_error(ob, (ModifierData *)smd_eval, "Out of memory");
return false;
@@ -1220,7 +1220,7 @@ static bool surfacedeformBind(Object *ob,
}
adj_result = buildAdjacencyMap(
- mpoly, medge, mloop, tpolys_num, tedges_num, vert_edges, adj_array, edge_polys);
+ mpoly, medge, mloop, target_polys_num, tedges_num, vert_edges, adj_array, edge_polys);
if (adj_result == MOD_SDEF_BIND_RESULT_NONMANY_ERR) {
BKE_modifier_set_error(
@@ -1233,7 +1233,8 @@ static bool surfacedeformBind(Object *ob,
}
smd_orig->mesh_verts_num = verts_num;
- smd_orig->polys_num = tpolys_num;
+ smd_orig->target_verts_num = target_verts_num;
+ smd_orig->target_polys_num = target_polys_num;
int defgrp_index;
MDeformVert *dvert;
@@ -1249,7 +1250,8 @@ static bool surfacedeformBind(Object *ob,
.medge = medge,
.mloop = mloop,
.looptri = BKE_mesh_runtime_looptri_ensure(target),
- .targetCos = MEM_malloc_arrayN(tverts_num, sizeof(float[3]), "SDefTargetBindVertArray"),
+ .targetCos = MEM_malloc_arrayN(
+ target_verts_num, sizeof(float[3]), "SDefTargetBindVertArray"),
.bind_verts = smd_orig->verts,
.vertexCos = vertexCos,
.falloff = smd_orig->falloff,
@@ -1268,7 +1270,7 @@ static bool surfacedeformBind(Object *ob,
invert_m4_m4(data.imat, smd_orig->mat);
- for (int i = 0; i < tverts_num; i++) {
+ for (int i = 0; i < target_verts_num; i++) {
mul_v3_m4v3(data.targetCos[i], smd_orig->mat, mvert[i].co);
}
@@ -1431,7 +1433,7 @@ static void surfacedeformModifier_do(ModifierData *md,
{
SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
Mesh *target;
- uint tverts_num, tpolys_num;
+ uint target_verts_num, target_polys_num;
/* Exit function if bind flag is not set (free bind data if any). */
if (!(smd->flags & MOD_SDEF_BIND)) {
@@ -1453,8 +1455,8 @@ static void surfacedeformModifier_do(ModifierData *md,
return;
}
- tverts_num = BKE_mesh_wrapper_vert_len(target);
- tpolys_num = BKE_mesh_wrapper_poly_len(target);
+ target_verts_num = BKE_mesh_wrapper_vert_len(target);
+ target_polys_num = BKE_mesh_wrapper_poly_len(target);
/* If not bound, execute bind. */
if (smd->verts == NULL) {
@@ -1473,25 +1475,62 @@ static void surfacedeformModifier_do(ModifierData *md,
/* Avoid converting edit-mesh data, binding is an exception. */
BKE_mesh_wrapper_ensure_mdata(target);
- if (!surfacedeformBind(
- ob, smd_orig, smd, vertexCos, verts_num, tpolys_num, tverts_num, target, mesh)) {
+ if (!surfacedeformBind(ob,
+ smd_orig,
+ smd,
+ vertexCos,
+ verts_num,
+ target_polys_num,
+ target_verts_num,
+ target,
+ mesh)) {
smd->flags &= ~MOD_SDEF_BIND;
}
/* Early abort, this is binding 'call', no need to perform whole evaluation. */
return;
}
- /* Poly count checks */
+ /* Geometry count on the deforming mesh. */
if (smd->mesh_verts_num != verts_num) {
BKE_modifier_set_error(
ob, md, "Vertices changed from %u to %u", smd->mesh_verts_num, verts_num);
return;
}
- if (smd->polys_num != tpolys_num) {
+
+ /* Geometry count on the target mesh. */
+ if (smd->target_polys_num != target_polys_num && smd->target_verts_num == 0) {
+ /* Change in the number of polygons does not really imply change in the vertex count, but
+ * this is how the modifier worked before the vertex count was known. Follow the legacy
+ * logic without requirement to re-bind the mesh. */
BKE_modifier_set_error(
- ob, md, "Target polygons changed from %u to %u", smd->polys_num, tpolys_num);
+ ob, md, "Target polygons changed from %u to %u", smd->target_polys_num, target_polys_num);
return;
}
+ if (smd->target_verts_num != 0 && smd->target_verts_num != target_verts_num) {
+ if (smd->target_verts_num > target_verts_num) {
+ /* Number of vertices on the target did reduce. There is no usable recovery from this. */
+ BKE_modifier_set_error(ob,
+ md,
+ "Target vertices changed from %u to %u",
+ smd->target_verts_num,
+ target_verts_num);
+ return;
+ }
+
+ /* Assume the increase in the vertex count means that the "new" vertices in the target mesh are
+ * added after the original ones. This covers typical case when target was at the subdivision
+ * level 0 and then subdivision was increased (i.e. for the render purposes). */
+
+ BKE_modifier_set_error(ob,
+ md,
+ "Target vertices changed from %u to %u, continuing anyway",
+ smd->target_verts_num,
+ target_verts_num);
+
+ /* In theory we only need the `smd->verts_num` vertices in the `targetCos` for evaluation, but
+ * it is not currently possible to request a subset of coordinates: the API expects that the
+ * caller needs coordinates of all vertices and asserts for it. */
+ }
/* Early out if modifier would not affect input at all - still *after* the sanity checks
* (and potential binding) above. */
@@ -1507,7 +1546,7 @@ static void surfacedeformModifier_do(ModifierData *md,
/* Actual vertex location update starts here */
SDefDeformData data = {
.bind_verts = smd->verts,
- .targetCos = MEM_malloc_arrayN(tverts_num, sizeof(float[3]), "SDefTargetVertArray"),
+ .targetCos = MEM_malloc_arrayN(target_verts_num, sizeof(float[3]), "SDefTargetVertArray"),
.vertexCos = vertexCos,
.dvert = dvert,
.defgrp_index = defgrp_index,
@@ -1516,7 +1555,8 @@ static void surfacedeformModifier_do(ModifierData *md,
};
if (data.targetCos != NULL) {
- BKE_mesh_wrapper_vert_coords_copy_with_mat4(target, data.targetCos, tverts_num, smd->mat);
+ BKE_mesh_wrapper_vert_coords_copy_with_mat4(
+ target, data.targetCos, target_verts_num, smd->mat);
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
diff --git a/source/blender/modifiers/intern/MOD_util.h b/source/blender/modifiers/intern/MOD_util.h
index aef254b1103..c37d6a3f2c1 100644
--- a/source/blender/modifiers/intern/MOD_util.h
+++ b/source/blender/modifiers/intern/MOD_util.h
@@ -11,6 +11,10 @@
#include "DEG_depsgraph_build.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct MDeformVert;
struct Mesh;
struct ModifierData;
@@ -51,3 +55,7 @@ void MOD_depsgraph_update_object_bone_relation(struct DepsNodeHandle *node,
struct Object *object,
const char *bonename,
const char *description);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h
index 3d3450d9252..5d782674f16 100644
--- a/source/blender/nodes/NOD_composite.h
+++ b/source/blender/nodes/NOD_composite.h
@@ -126,6 +126,8 @@ void register_node_type_cmp_planetrackdeform(void);
void register_node_type_cmp_cornerpin(void);
void register_node_type_cmp_separate_xyz(void);
void register_node_type_cmp_combine_xyz(void);
+void register_node_type_cmp_separate_color(void);
+void register_node_type_cmp_combine_color(void);
void node_cmp_rlayers_outputs(struct bNodeTree *ntree, struct bNode *node);
void node_cmp_rlayers_register_pass(struct bNodeTree *ntree,
diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h
index cde4b67e120..ad96fba1929 100644
--- a/source/blender/nodes/NOD_function.h
+++ b/source/blender/nodes/NOD_function.h
@@ -8,6 +8,7 @@ extern "C" {
void register_node_type_fn_align_euler_to_vector(void);
void register_node_type_fn_boolean_math(void);
+void register_node_type_fn_combine_color(void);
void register_node_type_fn_compare(void);
void register_node_type_fn_float_to_int(void);
void register_node_type_fn_input_bool(void);
@@ -19,6 +20,7 @@ void register_node_type_fn_input_vector(void);
void register_node_type_fn_random_value(void);
void register_node_type_fn_replace_string(void);
void register_node_type_fn_rotate_euler(void);
+void register_node_type_fn_separate_color(void);
void register_node_type_fn_slice_string(void);
void register_node_type_fn_string_length(void);
void register_node_type_fn_value_to_string(void);
diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
index 1ad859aa47b..4fbf5192222 100644
--- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
+++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
@@ -103,16 +103,16 @@ class GeometryValueLog : public ValueLog {
public:
struct MeshInfo {
- int tot_verts, tot_edges, tot_faces;
+ int verts_num, edges_num, faces_num;
};
struct CurveInfo {
- int tot_splines;
+ int splines_num;
};
struct PointCloudInfo {
- int tot_points;
+ int points_num;
};
struct InstancesInfo {
- int tot_instances;
+ int instances_num;
};
std::optional<MeshInfo> mesh_info;
diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h
index 4996f12e27d..1d1310360b8 100644
--- a/source/blender/nodes/NOD_shader.h
+++ b/source/blender/nodes/NOD_shader.h
@@ -43,6 +43,8 @@ void register_node_type_sh_vect_math(void);
void register_node_type_sh_squeeze(void);
void register_node_type_sh_dynamic(void);
void register_node_type_sh_invert(void);
+void register_node_type_sh_sepcolor(void);
+void register_node_type_sh_combcolor(void);
void register_node_type_sh_seprgb(void);
void register_node_type_sh_combrgb(void);
void register_node_type_sh_sephsv(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 5818fa631e7..7dd732e7fad 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -42,8 +42,8 @@ DefNode(ShaderNode, SH_NODE_MATH, def_math, "MATH",
DefNode(ShaderNode, SH_NODE_VECTOR_MATH, def_vector_math, "VECT_MATH", VectorMath, "Vector Math", "" )
DefNode(ShaderNode, SH_NODE_SQUEEZE, 0, "SQUEEZE", Squeeze, "Squeeze Value", "" )
DefNode(ShaderNode, SH_NODE_INVERT, 0, "INVERT", Invert, "Invert", "" )
-DefNode(ShaderNode, SH_NODE_SEPRGB, 0, "SEPRGB", SeparateRGB, "Separate RGB", "" )
-DefNode(ShaderNode, SH_NODE_COMBRGB, 0, "COMBRGB", CombineRGB, "Combine RGB", "" )
+DefNode(ShaderNode, SH_NODE_SEPRGB_LEGACY, 0, "SEPRGB", SeparateRGB, "Separate RGB", "" )
+DefNode(ShaderNode, SH_NODE_COMBRGB_LEGACY, 0, "COMBRGB", CombineRGB, "Combine RGB", "" )
DefNode(ShaderNode, SH_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue/Saturation", "" )
DefNode(ShaderNode, SH_NODE_OUTPUT_MATERIAL, def_sh_output, "OUTPUT_MATERIAL", OutputMaterial, "Material Output", "" )
@@ -106,8 +106,8 @@ DefNode(ShaderNode, SH_NODE_TEX_POINTDENSITY, def_sh_tex_pointdensity,"TEX
DefNode(ShaderNode, SH_NODE_TEX_COORD, def_sh_tex_coord, "TEX_COORD", TexCoord, "Texture Coordinate","" )
DefNode(ShaderNode, SH_NODE_VECTOR_ROTATE, def_sh_vector_rotate, "VECTOR_ROTATE", VectorRotate, "Vector Rotate", "" )
DefNode(ShaderNode, SH_NODE_VECT_TRANSFORM, def_sh_vect_transform, "VECT_TRANSFORM", VectorTransform, "Vector Transform", "" )
-DefNode(ShaderNode, SH_NODE_SEPHSV, 0, "SEPHSV", SeparateHSV, "Separate HSV", "" )
-DefNode(ShaderNode, SH_NODE_COMBHSV, 0, "COMBHSV", CombineHSV, "Combine HSV", "" )
+DefNode(ShaderNode, SH_NODE_SEPHSV_LEGACY, 0, "SEPHSV", SeparateHSV, "Separate HSV", "" )
+DefNode(ShaderNode, SH_NODE_COMBHSV_LEGACY, 0, "COMBHSV", CombineHSV, "Combine HSV", "" )
DefNode(ShaderNode, SH_NODE_UVMAP, def_sh_uvmap, "UVMAP", UVMap, "UV Map", "" )
DefNode(ShaderNode, SH_NODE_VERTEX_COLOR, def_sh_vertex_color, "VERTEX_COLOR", VertexColor, "Color Attribute", "" )
DefNode(ShaderNode, SH_NODE_UVALONGSTROKE, def_sh_uvalongstroke, "UVALONGSTROKE", UVAlongStroke, "UV Along Stroke", "" )
@@ -120,6 +120,8 @@ DefNode(ShaderNode, SH_NODE_TEX_IES, def_sh_tex_ies, "TEX
DefNode(ShaderNode, SH_NODE_TEX_WHITE_NOISE, def_sh_tex_white_noise, "TEX_WHITE_NOISE", TexWhiteNoise, "White Noise", "" )
DefNode(ShaderNode, SH_NODE_OUTPUT_AOV, def_sh_output_aov, "OUTPUT_AOV", OutputAOV, "AOV Output", "" )
DefNode(ShaderNode, SH_NODE_CURVE_FLOAT, def_float_curve, "CURVE_FLOAT", FloatCurve, "Float Curve", "" )
+DefNode(ShaderNode, SH_NODE_COMBINE_COLOR, def_sh_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "" )
+DefNode(ShaderNode, SH_NODE_SEPARATE_COLOR, def_sh_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "" )
DefNode(CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" )
DefNode(CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" )
@@ -137,8 +139,8 @@ DefNode(CompositorNode, CMP_NODE_MAP_VALUE, def_cmp_map_value, "MAP_VA
DefNode(CompositorNode, CMP_NODE_MAP_RANGE, def_cmp_map_range, "MAP_RANGE", MapRange, "Map Range", "" )
DefNode(CompositorNode, CMP_NODE_TIME, def_time, "TIME", Time, "Time Curve", "" )
DefNode(CompositorNode, CMP_NODE_VECBLUR, def_cmp_vector_blur, "VECBLUR", VecBlur, "Vector Blur", "" )
-DefNode(CompositorNode, CMP_NODE_SEPRGBA, 0, "SEPRGBA", SepRGBA, "Separate RGBA", "" )
-DefNode(CompositorNode, CMP_NODE_SEPHSVA, 0, "SEPHSVA", SepHSVA, "Separate HSVA", "" )
+DefNode(CompositorNode, CMP_NODE_SEPRGBA_LEGACY, 0, "SEPRGBA", SepRGBA, "Separate RGBA", "" )
+DefNode(CompositorNode, CMP_NODE_SEPHSVA_LEGACY, 0, "SEPHSVA", SepHSVA, "Separate HSVA", "" )
DefNode(CompositorNode, CMP_NODE_SETALPHA, def_cmp_set_alpha, "SETALPHA", SetAlpha, "Set Alpha", "" )
DefNode(CompositorNode, CMP_NODE_HUE_SAT, 0, "HUE_SAT", HueSat, "Hue Saturation Value","" )
DefNode(CompositorNode, CMP_NODE_IMAGE, def_cmp_image, "IMAGE", Image, "Image", "" )
@@ -149,16 +151,16 @@ DefNode(CompositorNode, CMP_NODE_OUTPUT_FILE, 0, "OUTPUT
DefNode(CompositorNode, CMP_NODE_TEXTURE, def_texture, "TEXTURE", Texture, "Texture", "" )
DefNode(CompositorNode, CMP_NODE_TRANSLATE, def_cmp_translate, "TRANSLATE", Translate, "Translate", "" )
DefNode(CompositorNode, CMP_NODE_ZCOMBINE, def_cmp_zcombine, "ZCOMBINE", Zcombine, "Z Combine", "" )
-DefNode(CompositorNode, CMP_NODE_COMBRGBA, 0, "COMBRGBA", CombRGBA, "Combine RGBA", "" )
+DefNode(CompositorNode, CMP_NODE_COMBRGBA_LEGACY,0, "COMBRGBA", CombRGBA, "Combine RGBA", "" )
DefNode(CompositorNode, CMP_NODE_DILATEERODE, def_cmp_dilate_erode, "DILATEERODE", DilateErode, "Dilate/Erode", "" )
DefNode(CompositorNode, CMP_NODE_INPAINT, def_cmp_inpaint, "INPAINT", Inpaint, "Inpaint", "" )
DefNode(CompositorNode, CMP_NODE_DESPECKLE, def_cmp_despeckle, "DESPECKLE", Despeckle, "Despeckle", "" )
DefNode(CompositorNode, CMP_NODE_ROTATE, def_cmp_rotate, "ROTATE", Rotate, "Rotate", "" )
DefNode(CompositorNode, CMP_NODE_SCALE, def_cmp_scale, "SCALE", Scale, "Scale", "" )
-DefNode(CompositorNode, CMP_NODE_SEPYCCA, def_cmp_ycc, "SEPYCCA", SepYCCA, "Separate YCbCrA", "" )
-DefNode(CompositorNode, CMP_NODE_COMBYCCA, def_cmp_ycc, "COMBYCCA", CombYCCA, "Combine YCbCrA", "" )
-DefNode(CompositorNode, CMP_NODE_SEPYUVA, 0, "SEPYUVA", SepYUVA, "Separate YUVA", "" )
-DefNode(CompositorNode, CMP_NODE_COMBYUVA, 0, "COMBYUVA", CombYUVA, "Combine YUVA", "" )
+DefNode(CompositorNode, CMP_NODE_SEPYCCA_LEGACY, def_cmp_ycc, "SEPYCCA", SepYCCA, "Separate YCbCrA", "" )
+DefNode(CompositorNode, CMP_NODE_COMBYCCA_LEGACY,def_cmp_ycc, "COMBYCCA", CombYCCA, "Combine YCbCrA", "" )
+DefNode(CompositorNode, CMP_NODE_SEPYUVA_LEGACY, 0, "SEPYUVA", SepYUVA, "Separate YUVA", "" )
+DefNode(CompositorNode, CMP_NODE_COMBYUVA_LEGACY,0, "COMBYUVA", CombYUVA, "Combine YUVA", "" )
DefNode(CompositorNode, CMP_NODE_DIFF_MATTE, def_cmp_diff_matte, "DIFF_MATTE", DiffMatte, "Difference Key", "" )
DefNode(CompositorNode, CMP_NODE_COLOR_SPILL, def_cmp_color_spill, "COLOR_SPILL", ColorSpill, "Color Spill", "" )
DefNode(CompositorNode, CMP_NODE_CHROMA_MATTE, def_cmp_chroma_matte, "CHROMA_MATTE", ChromaMatte, "Chroma Key", "" )
@@ -170,7 +172,7 @@ DefNode(CompositorNode, CMP_NODE_ID_MASK, def_cmp_id_mask, "ID_MAS
DefNode(CompositorNode, CMP_NODE_DOUBLEEDGEMASK, def_cmp_double_edge_mask,"DOUBLEEDGEMASK", DoubleEdgeMask, "Double Edge Mask", "" )
DefNode(CompositorNode, CMP_NODE_DEFOCUS, def_cmp_defocus, "DEFOCUS", Defocus, "Defocus", "" )
DefNode(CompositorNode, CMP_NODE_DISPLACE, 0, "DISPLACE", Displace, "Displace", "" )
-DefNode(CompositorNode, CMP_NODE_COMBHSVA, 0, "COMBHSVA", CombHSVA, "Combine HSVA", "" )
+DefNode(CompositorNode, CMP_NODE_COMBHSVA_LEGACY,0, "COMBHSVA", CombHSVA, "Combine HSVA", "" )
DefNode(CompositorNode, CMP_NODE_MATH, def_math, "MATH", Math, "Math", "" )
DefNode(CompositorNode, CMP_NODE_LUMA_MATTE, def_cmp_luma_matte, "LUMA_MATTE", LumaMatte, "Luminance Key", "" )
DefNode(CompositorNode, CMP_NODE_BRIGHTCONTRAST, def_cmp_brightcontrast, "BRIGHTCONTRAST", BrightContrast, "Bright/Contrast", "" )
@@ -215,9 +217,11 @@ DefNode(CompositorNode, CMP_NODE_EXPOSURE, 0, "EXPOSU
DefNode(CompositorNode, CMP_NODE_ANTIALIASING, def_cmp_antialiasing, "ANTIALIASING", AntiAliasing, "Anti-Aliasing", "" )
DefNode(CompositorNode, CMP_NODE_POSTERIZE, 0, "POSTERIZE", Posterize, "Posterize", "" )
DefNode(CompositorNode, CMP_NODE_CONVERT_COLOR_SPACE,def_cmp_convert_color_space, "CONVERT_COLORSPACE", ConvertColorSpace, "Color Space","" )
-DefNode(CompositorNode, CMP_NODE_SCENE_TIME, 0, "SCENE_TIME", SceneTime, "Scene Time", "" )
+DefNode(CompositorNode, CMP_NODE_SCENE_TIME, 0, "SCENE_TIME", SceneTime, "Scene Time", "" )
DefNode(CompositorNode, CMP_NODE_COMBINE_XYZ, 0, "COMBINE_XYZ", CombineXYZ, "Combine XYZ", "" )
DefNode(CompositorNode, CMP_NODE_SEPARATE_XYZ, 0, "SEPARATE_XYZ", SeparateXYZ, "Separate XYZ", "" )
+DefNode(CompositorNode, CMP_NODE_SEPARATE_COLOR, def_cmp_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "" )
+DefNode(CompositorNode, CMP_NODE_COMBINE_COLOR, def_cmp_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "" )
DefNode(TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" )
DefNode(TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" )
@@ -237,11 +241,13 @@ DefNode(TextureNode, TEX_NODE_VIEWER, 0, "VIEWER
DefNode(TextureNode, TEX_NODE_TRANSLATE, 0, "TRANSLATE", Translate, "Translate", "" )
DefNode(TextureNode, TEX_NODE_COORD, 0, "COORD", Coordinates, "Coordinates", "" )
DefNode(TextureNode, TEX_NODE_DISTANCE, 0, "DISTANCE", Distance, "Distance", "" )
-DefNode(TextureNode, TEX_NODE_COMPOSE, 0, "COMPOSE", Compose, "Combine RGBA", "" )
-DefNode(TextureNode, TEX_NODE_DECOMPOSE, 0, "DECOMPOSE", Decompose, "Separate RGBA", "" )
+DefNode(TextureNode, TEX_NODE_COMPOSE_LEGACY, 0, "COMPOSE", Compose, "Combine RGBA", "" )
+DefNode(TextureNode, TEX_NODE_DECOMPOSE_LEGACY,0, "DECOMPOSE", Decompose, "Separate RGBA", "" )
DefNode(TextureNode, TEX_NODE_VALTONOR, 0, "VALTONOR", ValToNor, "Value to Normal", "" )
DefNode(TextureNode, TEX_NODE_SCALE, 0, "SCALE", Scale, "Scale", "" )
DefNode(TextureNode, TEX_NODE_AT, 0, "AT", At, "At", "" )
+DefNode(TextureNode, TEX_NODE_COMBINE_COLOR, def_tex_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "" )
+DefNode(TextureNode, TEX_NODE_SEPARATE_COLOR, def_tex_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "" )
/* procedural textures */
DefNode(TextureNode, TEX_NODE_PROC+TEX_VORONOI, 0, "TEX_VORONOI", TexVoronoi, "Voronoi", "" )
DefNode(TextureNode, TEX_NODE_PROC+TEX_BLEND, 0, "TEX_BLEND", TexBlend, "Blend", "" )
@@ -256,6 +262,7 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DI
DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler To Vector", "")
DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
+DefNode(FunctionNode, FN_NODE_COMBINE_COLOR, def_fn_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "")
DefNode(FunctionNode, FN_NODE_COMPARE, def_compare, "COMPARE", Compare, "Compare", "")
DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "")
DefNode(FunctionNode, FN_NODE_INPUT_BOOL, def_fn_input_bool, "INPUT_BOOL", InputBool, "Boolean", "")
@@ -267,6 +274,7 @@ DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR",
DefNode(FunctionNode, FN_NODE_RANDOM_VALUE, def_fn_random_value, "RANDOM_VALUE", RandomValue, "Random Value", "")
DefNode(FunctionNode, FN_NODE_REPLACE_STRING, 0, "REPLACE_STRING", ReplaceString, "Replace String", "")
DefNode(FunctionNode, FN_NODE_ROTATE_EULER, def_fn_rotate_euler, "ROTATE_EULER", RotateEuler, "Rotate Euler", "")
+DefNode(FunctionNode, FN_NODE_SEPARATE_COLOR, def_fn_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "")
DefNode(FunctionNode, FN_NODE_SLICE_STRING, 0, "SLICE_STRING", SliceString, "Slice String", "")
DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "")
DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "")
diff --git a/source/blender/nodes/NOD_texture.h b/source/blender/nodes/NOD_texture.h
index 0f07b17f165..9a2dc705c0d 100644
--- a/source/blender/nodes/NOD_texture.h
+++ b/source/blender/nodes/NOD_texture.h
@@ -46,6 +46,8 @@ void register_node_type_tex_at(void);
void register_node_type_tex_compose(void);
void register_node_type_tex_decompose(void);
+void register_node_type_tex_combine_color(void);
+void register_node_type_tex_separate_color(void);
void register_node_type_tex_proc_voronoi(void);
void register_node_type_tex_proc_blend(void);
diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt
index 57f76f20ac6..c0100d77889 100644
--- a/source/blender/nodes/composite/CMakeLists.txt
+++ b/source/blender/nodes/composite/CMakeLists.txt
@@ -91,6 +91,7 @@ set(SRC
nodes/node_composite_rotate.cc
nodes/node_composite_scale.cc
nodes/node_composite_scene_time.cc
+ nodes/node_composite_sepcomb_color.cc
nodes/node_composite_sepcomb_hsva.cc
nodes/node_composite_sepcomb_rgba.cc
nodes/node_composite_sepcomb_xyz.cc
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
new file mode 100644
index 00000000000..b253656a628
--- /dev/null
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "node_composite_util.hh"
+
+static void node_cmp_combsep_color_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeCMPCombSepColor *data = MEM_cnew<NodeCMPCombSepColor>(__func__);
+ data->mode = CMP_NODE_COMBSEP_COLOR_RGB;
+ data->ycc_mode = BLI_YCC_ITU_BT709;
+ node->storage = data;
+}
+
+static void node_cmp_combsep_color_label(const ListBase *sockets, CMPNodeCombSepColorMode mode)
+{
+ bNodeSocket *sock1 = (bNodeSocket *)sockets->first;
+ bNodeSocket *sock2 = sock1->next;
+ bNodeSocket *sock3 = sock2->next;
+
+ node_sock_label_clear(sock1);
+ node_sock_label_clear(sock2);
+ node_sock_label_clear(sock3);
+
+ switch (mode) {
+ case CMP_NODE_COMBSEP_COLOR_RGB:
+ node_sock_label(sock1, "Red");
+ node_sock_label(sock2, "Green");
+ node_sock_label(sock3, "Blue");
+ break;
+ case CMP_NODE_COMBSEP_COLOR_HSV:
+ node_sock_label(sock1, "Hue");
+ node_sock_label(sock2, "Saturation");
+ node_sock_label(sock3, "Value");
+ break;
+ case CMP_NODE_COMBSEP_COLOR_HSL:
+ node_sock_label(sock1, "Hue");
+ node_sock_label(sock2, "Saturation");
+ node_sock_label(sock3, "Lightness");
+ break;
+ case CMP_NODE_COMBSEP_COLOR_YCC:
+ node_sock_label(sock1, "Y");
+ node_sock_label(sock2, "Cb");
+ node_sock_label(sock3, "Cr");
+ break;
+ case CMP_NODE_COMBSEP_COLOR_YUV:
+ node_sock_label(sock1, "Y");
+ node_sock_label(sock2, "U");
+ node_sock_label(sock3, "V");
+ break;
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+}
+
+/* **************** SEPARATE COLOR ******************** */
+
+namespace blender::nodes::node_composite_separate_color_cc {
+
+static void cmp_node_separate_color_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Float>(N_("Red"));
+ b.add_output<decl::Float>(N_("Green"));
+ b.add_output<decl::Float>(N_("Blue"));
+ b.add_output<decl::Float>(N_("Alpha"));
+}
+
+static void cmp_node_separate_color_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)node->storage;
+ node_cmp_combsep_color_label(&node->outputs, (CMPNodeCombSepColorMode)storage->mode);
+}
+
+} // namespace blender::nodes::node_composite_separate_color_cc
+
+void register_node_type_cmp_separate_color()
+{
+ namespace file_ns = blender::nodes::node_composite_separate_color_cc;
+
+ static bNodeType ntype;
+
+ cmp_node_type_base(&ntype, CMP_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_CONVERTER);
+ ntype.declare = file_ns::cmp_node_separate_color_declare;
+ node_type_init(&ntype, node_cmp_combsep_color_init);
+ node_type_storage(
+ &ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage);
+ node_type_update(&ntype, file_ns::cmp_node_separate_color_update);
+
+ nodeRegisterType(&ntype);
+}
+
+/* **************** COMBINE COLOR ******************** */
+
+namespace blender::nodes::node_composite_combine_color_cc {
+
+static void cmp_node_combine_color_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Float>(N_("Green"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR);
+ b.add_input<decl::Float>(N_("Blue"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR);
+ b.add_input<decl::Float>(N_("Alpha"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR);
+ b.add_output<decl::Color>(N_("Image"));
+}
+
+static void cmp_node_combine_color_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)node->storage;
+ node_cmp_combsep_color_label(&node->inputs, (CMPNodeCombSepColorMode)storage->mode);
+}
+
+} // namespace blender::nodes::node_composite_combine_color_cc
+
+void register_node_type_cmp_combine_color()
+{
+ namespace file_ns = blender::nodes::node_composite_combine_color_cc;
+
+ static bNodeType ntype;
+
+ cmp_node_type_base(&ntype, CMP_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_CONVERTER);
+ ntype.declare = file_ns::cmp_node_combine_color_declare;
+ node_type_init(&ntype, node_cmp_combsep_color_init);
+ node_type_storage(
+ &ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage);
+ node_type_update(&ntype, file_ns::cmp_node_combine_color_update);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc
index 349c27d876d..a0d2485ea5a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc
@@ -28,7 +28,7 @@ void register_node_type_cmp_sephsva()
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA, "Separate HSVA", NODE_CLASS_CONVERTER);
+ cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA_LEGACY, "Separate HSVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_sephsva_declare;
nodeRegisterType(&ntype);
}
@@ -54,7 +54,7 @@ void register_node_type_cmp_combhsva()
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA, "Combine HSVA", NODE_CLASS_CONVERTER);
+ cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA_LEGACY, "Combine HSVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combhsva_declare;
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc
index c46603be847..ae46681b0f4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc
@@ -27,7 +27,7 @@ void register_node_type_cmp_seprgba()
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA, "Separate RGBA", NODE_CLASS_CONVERTER);
+ cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA_LEGACY, "Separate RGBA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_seprgba_declare;
nodeRegisterType(&ntype);
@@ -54,7 +54,7 @@ void register_node_type_cmp_combrgba()
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA, "Combine RGBA", NODE_CLASS_CONVERTER);
+ cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA_LEGACY, "Combine RGBA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combrgba_declare;
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc
index 9b5c153fddf..a3c40b61e64 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc
@@ -33,7 +33,7 @@ void register_node_type_cmp_sepycca()
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA, "Separate YCbCrA", NODE_CLASS_CONVERTER);
+ cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA_LEGACY, "Separate YCbCrA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_sepycca_declare;
node_type_init(&ntype, file_ns::node_composit_init_mode_sepycca);
@@ -66,7 +66,7 @@ void register_node_type_cmp_combycca()
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA, "Combine YCbCrA", NODE_CLASS_CONVERTER);
+ cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA_LEGACY, "Combine YCbCrA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combycca_declare;
node_type_init(&ntype, file_ns::node_composit_init_mode_combycca);
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc
index e458c9cfb7e..7fdece5904d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc
@@ -28,7 +28,7 @@ void register_node_type_cmp_sepyuva()
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA, "Separate YUVA", NODE_CLASS_CONVERTER);
+ cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA_LEGACY, "Separate YUVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_sepyuva_declare;
nodeRegisterType(&ntype);
@@ -55,7 +55,7 @@ void register_node_type_cmp_combyuva()
static bNodeType ntype;
- cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA, "Combine YUVA", NODE_CLASS_CONVERTER);
+ cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA_LEGACY, "Combine YUVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combyuva_declare;
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/function/CMakeLists.txt b/source/blender/nodes/function/CMakeLists.txt
index 6ccc4c7bf5c..d03f1cb63ff 100644
--- a/source/blender/nodes/function/CMakeLists.txt
+++ b/source/blender/nodes/function/CMakeLists.txt
@@ -20,6 +20,7 @@ set(INC
set(SRC
nodes/node_fn_align_euler_to_vector.cc
nodes/node_fn_boolean_math.cc
+ nodes/node_fn_combine_color.cc
nodes/node_fn_compare.cc
nodes/node_fn_float_to_int.cc
nodes/node_fn_input_bool.cc
@@ -31,6 +32,7 @@ set(SRC
nodes/node_fn_random_value.cc
nodes/node_fn_replace_string.cc
nodes/node_fn_rotate_euler.cc
+ nodes/node_fn_separate_color.cc
nodes/node_fn_slice_string.cc
nodes/node_fn_string_length.cc
nodes/node_fn_value_to_string.cc
diff --git a/source/blender/nodes/function/nodes/node_fn_combine_color.cc b/source/blender/nodes/function/nodes/node_fn_combine_color.cc
new file mode 100644
index 00000000000..c5fd3ce38a1
--- /dev/null
+++ b/source/blender/nodes/function/nodes/node_fn_combine_color.cc
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "node_function_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+namespace blender::nodes {
+
+NODE_STORAGE_FUNCS(NodeCombSepColor)
+
+static void fn_node_combine_color_declare(NodeDeclarationBuilder &b)
+{
+ b.is_function_node();
+ b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Float>(N_("Green"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR);
+ b.add_input<decl::Float>(N_("Blue"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR);
+ b.add_input<decl::Float>(N_("Alpha"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR);
+ b.add_output<decl::Color>(N_("Color"));
+};
+
+static void fn_node_combine_color_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
+}
+
+static void fn_node_combine_color_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeCombSepColor &storage = node_storage(*node);
+ node_combsep_color_label(&node->inputs, (NodeCombSepColorMode)storage.mode);
+}
+
+static void fn_node_combine_color_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeCombSepColor *data = MEM_cnew<NodeCombSepColor>(__func__);
+ data->mode = NODE_COMBSEP_COLOR_RGB;
+ node->storage = data;
+}
+
+static const fn::MultiFunction *get_multi_function(bNode &bnode)
+{
+ const NodeCombSepColor &storage = node_storage(bnode);
+
+ static fn::CustomMF_SI_SI_SI_SI_SO<float, float, float, float, ColorGeometry4f> rgba_fn{
+ "RGB", [](float r, float g, float b, float a) { return ColorGeometry4f(r, g, b, a); }};
+ static fn::CustomMF_SI_SI_SI_SI_SO<float, float, float, float, ColorGeometry4f> hsva_fn{
+ "HSV", [](float h, float s, float v, float a) {
+ ColorGeometry4f r_color;
+ hsv_to_rgb(h, s, v, &r_color.r, &r_color.g, &r_color.b);
+ r_color.a = a;
+ return r_color;
+ }};
+ static fn::CustomMF_SI_SI_SI_SI_SO<float, float, float, float, ColorGeometry4f> hsla_fn{
+ "HSL", [](float h, float s, float l, float a) {
+ ColorGeometry4f color;
+ hsl_to_rgb(h, s, l, &color.r, &color.g, &color.b);
+ color.a = a;
+ return color;
+ }};
+
+ switch (storage.mode) {
+ case NODE_COMBSEP_COLOR_RGB:
+ return &rgba_fn;
+ case NODE_COMBSEP_COLOR_HSV:
+ return &hsva_fn;
+ case NODE_COMBSEP_COLOR_HSL:
+ return &hsla_fn;
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+static void fn_node_combine_color_build_multi_function(NodeMultiFunctionBuilder &builder)
+{
+ const fn::MultiFunction *fn = get_multi_function(builder.node());
+ builder.set_matching_fn(fn);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_fn_combine_color(void)
+{
+ static bNodeType ntype;
+
+ fn_node_type_base(&ntype, FN_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_CONVERTER);
+ ntype.declare = blender::nodes::fn_node_combine_color_declare;
+ node_type_update(&ntype, blender::nodes::fn_node_combine_color_update);
+ node_type_init(&ntype, blender::nodes::fn_node_combine_color_init);
+ node_type_storage(
+ &ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage);
+ ntype.build_multi_function = blender::nodes::fn_node_combine_color_build_multi_function;
+ ntype.draw_buttons = blender::nodes::fn_node_combine_color_layout;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/function/nodes/node_fn_separate_color.cc b/source/blender/nodes/function/nodes/node_fn_separate_color.cc
new file mode 100644
index 00000000000..1701dfdc6fa
--- /dev/null
+++ b/source/blender/nodes/function/nodes/node_fn_separate_color.cc
@@ -0,0 +1,205 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "node_function_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+namespace blender::nodes {
+
+NODE_STORAGE_FUNCS(NodeCombSepColor)
+
+static void fn_node_separate_color_declare(NodeDeclarationBuilder &b)
+{
+ b.is_function_node();
+ b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_output<decl::Float>(N_("Red"));
+ b.add_output<decl::Float>(N_("Green"));
+ b.add_output<decl::Float>(N_("Blue"));
+ b.add_output<decl::Float>(N_("Alpha"));
+};
+
+static void fn_node_separate_color_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
+}
+
+static void fn_node_separate_color_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeCombSepColor &storage = node_storage(*node);
+ node_combsep_color_label(&node->outputs, (NodeCombSepColorMode)storage.mode);
+}
+
+static void fn_node_separate_color_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeCombSepColor *data = MEM_cnew<NodeCombSepColor>(__func__);
+ data->mode = NODE_COMBSEP_COLOR_RGB;
+ node->storage = data;
+}
+
+class SeparateRGBAFunction : public fn::MultiFunction {
+ public:
+ SeparateRGBAFunction()
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Separate Color"};
+ signature.single_input<ColorGeometry4f>("Color");
+ signature.single_output<float>("Red");
+ signature.single_output<float>("Green");
+ signature.single_output<float>("Blue");
+ signature.single_output<float>("Alpha");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<ColorGeometry4f> &colors = params.readonly_single_input<ColorGeometry4f>(0,
+ "Color");
+ MutableSpan<float> red = params.uninitialized_single_output<float>(1, "Red");
+ MutableSpan<float> green = params.uninitialized_single_output<float>(2, "Green");
+ MutableSpan<float> blue = params.uninitialized_single_output<float>(3, "Blue");
+ MutableSpan<float> alpha = params.uninitialized_single_output_if_required<float>(4, "Alpha");
+
+ for (int64_t i : mask) {
+ red[i] = colors[i].r;
+ green[i] = colors[i].g;
+ blue[i] = colors[i].b;
+ }
+
+ if (!alpha.is_empty()) {
+ for (int64_t i : mask) {
+ alpha[i] = colors[i].a;
+ }
+ }
+ }
+};
+
+class SeparateHSVAFunction : public fn::MultiFunction {
+ public:
+ SeparateHSVAFunction()
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Separate Color"};
+ signature.single_input<ColorGeometry4f>("Color");
+ signature.single_output<float>("Hue");
+ signature.single_output<float>("Saturation");
+ signature.single_output<float>("Value");
+ signature.single_output<float>("Alpha");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<ColorGeometry4f> &colors = params.readonly_single_input<ColorGeometry4f>(0,
+ "Color");
+ MutableSpan<float> hue = params.uninitialized_single_output<float>(1, "Hue");
+ MutableSpan<float> saturation = params.uninitialized_single_output<float>(2, "Saturation");
+ MutableSpan<float> value = params.uninitialized_single_output<float>(3, "Value");
+ MutableSpan<float> alpha = params.uninitialized_single_output_if_required<float>(4, "Alpha");
+
+ for (int64_t i : mask) {
+ rgb_to_hsv(colors[i].r, colors[i].g, colors[i].b, &hue[i], &saturation[i], &value[i]);
+ }
+
+ if (!alpha.is_empty()) {
+ for (int64_t i : mask) {
+ alpha[i] = colors[i].a;
+ }
+ }
+ }
+};
+
+class SeparateHSLAFunction : public fn::MultiFunction {
+ public:
+ SeparateHSLAFunction()
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Separate Color"};
+ signature.single_input<ColorGeometry4f>("Color");
+ signature.single_output<float>("Hue");
+ signature.single_output<float>("Saturation");
+ signature.single_output<float>("Lightness");
+ signature.single_output<float>("Alpha");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<ColorGeometry4f> &colors = params.readonly_single_input<ColorGeometry4f>(0,
+ "Color");
+ MutableSpan<float> hue = params.uninitialized_single_output<float>(1, "Hue");
+ MutableSpan<float> saturation = params.uninitialized_single_output<float>(2, "Saturation");
+ MutableSpan<float> lightness = params.uninitialized_single_output<float>(3, "Lightness");
+ MutableSpan<float> alpha = params.uninitialized_single_output_if_required<float>(4, "Alpha");
+
+ for (int64_t i : mask) {
+ rgb_to_hsl(colors[i].r, colors[i].g, colors[i].b, &hue[i], &saturation[i], &lightness[i]);
+ }
+
+ if (!alpha.is_empty()) {
+ for (int64_t i : mask) {
+ alpha[i] = colors[i].a;
+ }
+ }
+ }
+};
+
+static void fn_node_separate_color_build_multi_function(NodeMultiFunctionBuilder &builder)
+{
+ const NodeCombSepColor &storage = node_storage(builder.node());
+
+ switch (storage.mode) {
+ case NODE_COMBSEP_COLOR_RGB: {
+ static SeparateRGBAFunction fn;
+ builder.set_matching_fn(fn);
+ break;
+ }
+ case NODE_COMBSEP_COLOR_HSV: {
+ static SeparateHSVAFunction fn;
+ builder.set_matching_fn(fn);
+ break;
+ }
+ case NODE_COMBSEP_COLOR_HSL: {
+ static SeparateHSLAFunction fn;
+ builder.set_matching_fn(fn);
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_fn_separate_color(void)
+{
+ static bNodeType ntype;
+
+ fn_node_type_base(&ntype, FN_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_CONVERTER);
+ ntype.declare = blender::nodes::fn_node_separate_color_declare;
+ node_type_update(&ntype, blender::nodes::fn_node_separate_color_update);
+ node_type_init(&ntype, blender::nodes::fn_node_separate_color_init);
+ node_type_storage(
+ &ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage);
+ ntype.build_multi_function = blender::nodes::fn_node_separate_color_build_multi_function;
+ ntype.draw_buttons = blender::nodes::fn_node_separate_color_layout;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
index e081e007c81..38e914b9a9f 100644
--- a/source/blender/nodes/geometry/node_geometry_tree.cc
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -110,7 +110,7 @@ void register_node_tree_type_geo()
tt->type = NTREE_GEOMETRY;
strcpy(tt->idname, "GeometryNodeTree");
strcpy(tt->ui_name, N_("Geometry Node Editor"));
- tt->ui_icon = ICON_NODETREE;
+ tt->ui_icon = ICON_GEOMETRY_NODES;
strcpy(tt->ui_description, N_("Geometry nodes"));
tt->rna_ext.srna = &RNA_GeometryNodeTree;
tt->update = geometry_node_tree_update;
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh
index 7af3159bbf8..8f20da66c3b 100644
--- a/source/blender/nodes/geometry/node_geometry_util.hh
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -57,8 +57,6 @@ Mesh *create_cylinder_or_cone_mesh(float radius_top,
GeometryNodeMeshCircleFillType fill_type,
ConeAttributeOutputs &attribute_outputs);
-Mesh *create_cuboid_mesh(float3 size, int verts_x, int verts_y, int verts_z);
-
/**
* Copies the point domain attributes from `in_component` that are in the mask to `out_component`.
*/
@@ -81,14 +79,4 @@ void separate_geometry(GeometrySet &geometry_set,
std::optional<CustomDataType> node_data_type_to_custom_data_type(eNodeSocketDatatype type);
std::optional<CustomDataType> node_socket_to_custom_data_type(const bNodeSocket &socket);
-class SplineLengthFieldInput final : public GeometryFieldInput {
- public:
- SplineLengthFieldInput();
- GVArray get_varray_for_context(const GeometryComponent &component,
- AttributeDomain domain,
- IndexMask mask) const final;
- uint64_t hash() const override;
- bool is_equal_to(const fn::FieldNode &other) const override;
-};
-
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
index ea26eec0c15..b29831ceeb6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
@@ -217,16 +217,16 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
IndexMask UNUSED(mask)) const final
{
const GeometryComponentFieldContext field_context{component, source_domain_};
- const int domain_size = component.attribute_domain_size(field_context.domain());
+ const int domain_num = component.attribute_domain_num(field_context.domain());
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ fn::FieldEvaluator evaluator{field_context, domain_num};
evaluator.add(input_);
evaluator.add(group_index_);
evaluator.evaluate();
const VArray<T> &values = evaluator.get_evaluated<T>(0);
const VArray<int> &group_indices = evaluator.get_evaluated<int>(1);
- Array<T> accumulations_out(domain_size);
+ Array<T> accumulations_out(domain_num);
if (group_indices.is_single()) {
T accumulation = T();
@@ -303,9 +303,9 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput {
IndexMask UNUSED(mask)) const final
{
const GeometryComponentFieldContext field_context{component, source_domain_};
- const int domain_size = component.attribute_domain_size(field_context.domain());
+ const int domain_num = component.attribute_domain_num(field_context.domain());
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ fn::FieldEvaluator evaluator{field_context, domain_num};
evaluator.add(input_);
evaluator.add(group_index_);
evaluator.evaluate();
@@ -317,10 +317,10 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput {
for (const int i : values.index_range()) {
accumulation = values[i] + accumulation;
}
- return VArray<T>::ForSingle(accumulation, domain_size);
+ return VArray<T>::ForSingle(accumulation, domain_num);
}
- Array<T> accumulations_out(domain_size);
+ Array<T> accumulations_out(domain_num);
Map<int, T> accumulations;
for (const int i : values.index_range()) {
T &value = accumulations.lookup_or_add_default(group_indices[i]);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
index 45a6aabeb03..18cf005c965 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
@@ -112,8 +112,8 @@ static void try_capture_field_on_geometry(GeometryComponent &component,
const GField &field)
{
GeometryComponentFieldContext field_context{component, domain};
- const int domain_size = component.attribute_domain_size(domain);
- const IndexMask mask{IndexMask(domain_size)};
+ const int domain_num = component.attribute_domain_num(domain);
+ const IndexMask mask{IndexMask(domain_num)};
const CustomDataType data_type = bke::cpp_type_to_custom_data_type(field.cpp_type());
OutputAttribute output_attribute = component.attribute_try_get_for_output_only(
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
index b3fe9d160b3..8ab0eb678e7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
@@ -72,11 +72,11 @@ static void node_geo_exec(GeoNodeExecParams params)
case GEO_COMPONENT_TYPE_MESH: {
if (geometry_set.has_mesh()) {
const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
- params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT));
- params.set_output("Edge Count", component->attribute_domain_size(ATTR_DOMAIN_EDGE));
- params.set_output("Face Count", component->attribute_domain_size(ATTR_DOMAIN_FACE));
+ params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT));
+ params.set_output("Edge Count", component->attribute_domain_num(ATTR_DOMAIN_EDGE));
+ params.set_output("Face Count", component->attribute_domain_num(ATTR_DOMAIN_FACE));
params.set_output("Face Corner Count",
- component->attribute_domain_size(ATTR_DOMAIN_CORNER));
+ component->attribute_domain_num(ATTR_DOMAIN_CORNER));
}
else {
params.set_default_remaining_outputs();
@@ -86,8 +86,8 @@ static void node_geo_exec(GeoNodeExecParams params)
case GEO_COMPONENT_TYPE_CURVE: {
if (geometry_set.has_curves()) {
const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>();
- params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT));
- params.set_output("Spline Count", component->attribute_domain_size(ATTR_DOMAIN_CURVE));
+ params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT));
+ params.set_output("Spline Count", component->attribute_domain_num(ATTR_DOMAIN_CURVE));
}
else {
params.set_default_remaining_outputs();
@@ -98,7 +98,7 @@ static void node_geo_exec(GeoNodeExecParams params)
if (geometry_set.has_pointcloud()) {
const PointCloudComponent *component =
geometry_set.get_component_for_read<PointCloudComponent>();
- params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT));
+ params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT));
}
else {
params.set_default_remaining_outputs();
@@ -109,8 +109,7 @@ static void node_geo_exec(GeoNodeExecParams params)
if (geometry_set.has_instances()) {
const InstancesComponent *component =
geometry_set.get_component_for_read<InstancesComponent>();
- params.set_output("Instance Count",
- component->attribute_domain_size(ATTR_DOMAIN_INSTANCE));
+ params.set_output("Instance Count", component->attribute_domain_num(ATTR_DOMAIN_INSTANCE));
}
else {
params.set_default_remaining_outputs();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
index 1153f18ffd4..c7f65a68d60 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
@@ -197,9 +197,9 @@ static void node_geo_exec(GeoNodeExecParams params)
for (const GeometryComponent *component : components) {
if (component->attribute_domain_supported(domain)) {
GeometryComponentFieldContext field_context{*component, domain};
- const int domain_size = component->attribute_domain_size(domain);
+ const int domain_num = component->attribute_domain_num(domain);
- fn::FieldEvaluator data_evaluator{field_context, domain_size};
+ fn::FieldEvaluator data_evaluator{field_context, domain_num};
data_evaluator.add(input_field);
data_evaluator.set_selection(selection_field);
data_evaluator.evaluate();
@@ -275,9 +275,9 @@ static void node_geo_exec(GeoNodeExecParams params)
for (const GeometryComponent *component : components) {
if (component->attribute_domain_supported(domain)) {
GeometryComponentFieldContext field_context{*component, domain};
- const int domain_size = component->attribute_domain_size(domain);
+ const int domain_num = component->attribute_domain_num(domain);
- fn::FieldEvaluator data_evaluator{field_context, domain_size};
+ fn::FieldEvaluator data_evaluator{field_context, domain_num};
data_evaluator.add(input_field);
data_evaluator.set_selection(selection_field);
data_evaluator.evaluate();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
index 558129fb384..00b10cc8a2f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "GEO_mesh_primitive_cuboid.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_bounding_box_cc {
@@ -53,7 +55,7 @@ static void node_geo_exec(GeoNodeExecParams params)
else {
const float3 scale = sub_max - sub_min;
const float3 center = sub_min + scale / 2.0f;
- Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2);
+ Mesh *mesh = geometry::create_cuboid_mesh(scale, 2, 2, 2, "uv_map");
transform_mesh(*mesh, center, float3(0), float3(1));
sub_geometry.replace_mesh(mesh);
sub_geometry.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
index 09d0f13c50d..31f706c497c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
@@ -138,14 +138,14 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
{
int span_count = 0;
int count = 0;
- int total_size = 0;
+ int total_num = 0;
Span<float3> positions_span;
if (geometry_set.has_mesh()) {
count++;
const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
- total_size += component->attribute_domain_size(ATTR_DOMAIN_POINT);
+ total_num += component->attribute_domain_num(ATTR_DOMAIN_POINT);
}
if (geometry_set.has_pointcloud()) {
@@ -155,7 +155,7 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
geometry_set.get_component_for_read<PointCloudComponent>();
VArray<float3> varray = component->attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
- total_size += varray.size();
+ total_num += varray.size();
positions_span = varray.get_internal_span();
}
@@ -165,7 +165,7 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
const Curves &curves_id = *geometry_set.get_curves_for_read();
const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
positions_span = curves.evaluated_positions();
- total_size += positions_span.size();
+ total_num += positions_span.size();
}
if (count == 0) {
@@ -178,7 +178,7 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
return hull_from_bullet(nullptr, positions_span);
}
- Array<float3> positions(total_size);
+ Array<float3> positions(total_num);
int offset = 0;
if (geometry_set.has_mesh()) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
index 95ea978541c..fb8a488ddae 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
@@ -106,13 +106,13 @@ static float3 get_center(const float3 vec_pos2prev, const FilletData &fd, const
/* Calculate the direction vectors from each vertex to their previous vertex. */
static Array<float3> calculate_directions(const Span<float3> positions)
{
- const int size = positions.size();
- Array<float3> directions(size);
+ const int num = positions.size();
+ Array<float3> directions(num);
- for (const int i : IndexRange(size - 1)) {
+ for (const int i : IndexRange(num - 1)) {
directions[i] = math::normalize(positions[i + 1] - positions[i]);
}
- directions[size - 1] = math::normalize(positions[0] - positions[size - 1]);
+ directions[num - 1] = math::normalize(positions[0] - positions[num - 1]);
return directions;
}
@@ -120,11 +120,11 @@ static Array<float3> calculate_directions(const Span<float3> positions)
/* Calculate the axes around which the fillet is built. */
static Array<float3> calculate_axes(const Span<float3> directions)
{
- const int size = directions.size();
- Array<float3> axes(size);
+ const int num = directions.size();
+ Array<float3> axes(num);
- axes[0] = math::normalize(math::cross(-directions[size - 1], directions[0]));
- for (const int i : IndexRange(1, size - 1)) {
+ axes[0] = math::normalize(math::cross(-directions[num - 1], directions[0]));
+ for (const int i : IndexRange(1, num - 1)) {
axes[i] = math::normalize(math::cross(-directions[i - 1], directions[i]));
}
@@ -134,11 +134,11 @@ static Array<float3> calculate_axes(const Span<float3> directions)
/* Calculate the angle of the arc formed by the fillet. */
static Array<float> calculate_angles(const Span<float3> directions)
{
- const int size = directions.size();
- Array<float> angles(size);
+ const int num = directions.size();
+ Array<float> angles(num);
- angles[0] = M_PI - angle_v3v3(-directions[size - 1], directions[0]);
- for (const int i : IndexRange(1, size - 1)) {
+ angles[0] = M_PI - angle_v3v3(-directions[num - 1], directions[0]);
+ for (const int i : IndexRange(1, num - 1)) {
angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]);
}
@@ -147,18 +147,18 @@ static Array<float> calculate_angles(const Span<float3> directions)
/* Calculate the segment count in each filleted arc. */
static Array<int> calculate_counts(const FilletParam &fillet_param,
- const int size,
+ const int num,
const int spline_offset,
const bool cyclic)
{
- Array<int> counts(size, 1);
+ Array<int> counts(num, 1);
if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) {
- for (const int i : IndexRange(size)) {
+ for (const int i : IndexRange(num)) {
counts[i] = fillet_param.counts[spline_offset + i];
}
}
if (!cyclic) {
- counts[0] = counts[size - 1] = 0;
+ counts[0] = counts[num - 1] = 0;
}
return counts;
@@ -166,17 +166,17 @@ static Array<int> calculate_counts(const FilletParam &fillet_param,
/* Calculate the radii for the vertices to be filleted. */
static Array<float> calculate_radii(const FilletParam &fillet_param,
- const int size,
+ const int num,
const int spline_offset)
{
- Array<float> radii(size, 0.0f);
+ Array<float> radii(num, 0.0f);
if (fillet_param.limit_radius) {
- for (const int i : IndexRange(size)) {
+ for (const int i : IndexRange(num)) {
radii[i] = std::max(fillet_param.radii[spline_offset + i], 0.0f);
}
}
else {
- for (const int i : IndexRange(size)) {
+ for (const int i : IndexRange(num)) {
radii[i] = fillet_param.radii[spline_offset + i];
}
}
@@ -207,15 +207,15 @@ static FilletData calculate_fillet_data(const Spline &spline,
MutableSpan<int> point_counts,
const int spline_offset)
{
- const int size = spline.size();
+ const int num = spline.size();
FilletData fd;
fd.directions = calculate_directions(spline.positions());
fd.positions = spline.positions();
fd.axes = calculate_axes(fd.directions);
fd.angles = calculate_angles(fd.directions);
- fd.counts = calculate_counts(fillet_param, size, spline_offset, spline.is_cyclic());
- fd.radii = calculate_radii(fillet_param, size, spline_offset);
+ fd.counts = calculate_counts(fillet_param, num, spline_offset, spline.is_cyclic());
+ fd.radii = calculate_radii(fillet_param, num, spline_offset);
added_count = calculate_point_counts(point_counts, fd.radii, fd.counts);
@@ -229,19 +229,19 @@ static void limit_radii(FilletData &fd, const bool cyclic)
Span<float> angles(fd.angles);
Span<float3> positions(fd.positions);
- const int size = radii.size();
- const int fillet_count = cyclic ? size : size - 2;
+ const int num = radii.size();
+ const int fillet_count = cyclic ? num : num - 2;
const int start = cyclic ? 0 : 1;
- Array<float> max_radii(size, FLT_MAX);
+ Array<float> max_radii(num, FLT_MAX);
if (cyclic) {
/* Calculate lengths between adjacent control points. */
- const float len_prev = math::distance(positions[0], positions[size - 1]);
+ const float len_prev = math::distance(positions[0], positions[num - 1]);
const float len_next = math::distance(positions[0], positions[1]);
/* Calculate tangent lengths of fillets in control points. */
const float tan_len = radii[0] * tan(angles[0] / 2.0f);
- const float tan_len_prev = radii[size - 1] * tan(angles[size - 1] / 2.0f);
+ const float tan_len_prev = radii[num - 1] * tan(angles[num - 1] / 2.0f);
const float tan_len_next = radii[1] * tan(angles[1] / 2.0f);
float factor_prev = 1.0f, factor_next = 1.0f;
@@ -255,12 +255,12 @@ static void limit_radii(FilletData &fd, const bool cyclic)
/* Scale max radii by calculated factors. */
max_radii[0] = radii[0] * std::min(factor_next, factor_prev);
max_radii[1] = radii[1] * factor_next;
- max_radii[size - 1] = radii[size - 1] * factor_prev;
+ max_radii[num - 1] = radii[num - 1] * factor_prev;
}
/* Initialize max_radii to largest possible radii. */
float prev_dist = math::distance(positions[1], positions[0]);
- for (const int i : IndexRange(1, size - 2)) {
+ for (const int i : IndexRange(1, num - 2)) {
const float temp_dist = math::distance(positions[i], positions[i + 1]);
max_radii[i] = std::min(prev_dist, temp_dist) / tan(angles[i] / 2.0f);
prev_dist = temp_dist;
@@ -282,7 +282,7 @@ static void limit_radii(FilletData &fd, const bool cyclic)
}
/* Assign the max_radii to the fillet data's radii. */
- for (const int i : IndexRange(size)) {
+ for (const int i : IndexRange(num)) {
radii[i] = std::min(radii[i], max_radii[i]);
}
}
@@ -358,10 +358,10 @@ static void update_bezier_positions(const FilletData &fd,
Span<float3> positions(fd.positions);
Span<float3> directions(fd.directions);
- const int size = radii.size();
+ const int num = radii.size();
int i_dst = 0;
- for (const int i_src : IndexRange(size)) {
+ for (const int i_src : IndexRange(num)) {
const int count = point_counts[i_src];
/* Skip if the point count for the vertex is 1. */
@@ -385,7 +385,7 @@ static void update_bezier_positions(const FilletData &fd,
/* Position the end points of the arc and their handles. */
const int end_i = i_dst + count - 1;
- const float3 prev_dir = i_src == 0 ? -directions[size - 1] : -directions[i_src - 1];
+ const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1];
const float3 next_dir = directions[i_src];
dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir;
dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir;
@@ -442,10 +442,10 @@ static void update_poly_positions(const FilletData &fd,
Span<float3> positions(fd.positions);
Span<float3> directions(fd.directions);
- const int size = radii.size();
+ const int num = radii.size();
int i_dst = 0;
- for (const int i_src : IndexRange(size)) {
+ for (const int i_src : IndexRange(num)) {
const int count = point_counts[i_src];
/* Skip if the point count for the vertex is 1. */
@@ -460,7 +460,7 @@ static void update_poly_positions(const FilletData &fd,
/* Position the end points of the arc. */
const int end_i = i_dst + count - 1;
- const float3 prev_dir = i_src == 0 ? -directions[size - 1] : -directions[i_src - 1];
+ const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1];
const float3 next_dir = directions[i_src];
dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir;
dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir;
@@ -487,15 +487,15 @@ static SplinePtr fillet_spline(const Spline &spline,
const FilletParam &fillet_param,
const int spline_offset)
{
- const int size = spline.size();
+ const int num = spline.size();
const bool cyclic = spline.is_cyclic();
- if (size < 3) {
+ if (num < 3) {
return spline.copy();
}
/* Initialize the point_counts with 1s (at least one vertex on dst for each vertex on src). */
- Array<int> point_counts(size, 1);
+ Array<int> point_counts(num, 1);
int added_count = 0;
/* Update point_counts array and added_count. */
@@ -505,7 +505,7 @@ static SplinePtr fillet_spline(const Spline &spline,
limit_radii(fd, cyclic);
}
- const int total_points = added_count + size;
+ const int total_points = added_count + num;
const Array<int> dst_to_src = create_dst_to_src_map(point_counts, total_points);
SplinePtr dst_spline_ptr = spline.copy_only_settings();
(*dst_spline_ptr).resize(total_points);
@@ -581,8 +581,8 @@ static void calculate_curve_fillet(GeometrySet &geometry_set,
CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
- fn::FieldEvaluator field_evaluator{field_context, domain_size};
+ const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
+ fn::FieldEvaluator field_evaluator{field_context, domain_num};
field_evaluator.add(radius_field);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
index e0348f27e51..d9cc8bcf023 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -1,12 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BLI_array.hh"
-#include "BLI_index_mask_ops.hh"
-#include "BLI_length_parameterize.hh"
-#include "BLI_task.hh"
-#include "BLI_timeit.hh"
+#include "GEO_resample_curves.hh"
-#include "BKE_attribute_math.hh"
#include "BKE_curves.hh"
#include "UI_interface.h"
@@ -56,556 +51,6 @@ static void node_update(bNodeTree *ntree, bNode *node)
nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH);
}
-/** Returns the number of evaluated points in each curve. Used to deselect curves with none. */
-class EvaluatedCountFieldInput final : public GeometryFieldInput {
- public:
- EvaluatedCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Evaluated Point Count")
- {
- category_ = Category::Generated;
- }
-
- GVArray get_varray_for_context(const GeometryComponent &component,
- const AttributeDomain domain,
- IndexMask UNUSED(mask)) const final
- {
- if (component.type() == GEO_COMPONENT_TYPE_CURVE && domain == ATTR_DOMAIN_CURVE &&
- !component.is_empty()) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- const Curves &curves_id = *curve_component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- curves.ensure_evaluated_offsets();
- return VArray<int>::ForFunc(curves.curves_num(), [&](const int64_t index) -> int {
- return curves.evaluated_points_for_curve(index).size();
- });
- }
- return {};
- }
-
- uint64_t hash() const override
- {
- /* Some random constant hash. */
- return 234905872379865;
- }
-
- bool is_equal_to(const fn::FieldNode &other) const override
- {
- return dynamic_cast<const EvaluatedCountFieldInput *>(&other) != nullptr;
- }
-};
-
-/**
- * Return true if the attribute should be copied/interpolated to the result curves.
- * Don't output attributes that correspond to curve types that have no curves in the result.
- */
-static bool interpolate_attribute_to_curves(const AttributeIDRef &attribute_id,
- const std::array<int, CURVE_TYPES_NUM> &type_counts)
-{
- if (!attribute_id.is_named()) {
- return true;
- }
- if (ELEM(attribute_id.name(),
- "handle_type_left",
- "handle_type_right",
- "handle_left",
- "handle_right")) {
- return type_counts[CURVE_TYPE_BEZIER] != 0;
- }
- if (ELEM(attribute_id.name(), "nurbs_weight")) {
- return type_counts[CURVE_TYPE_NURBS] != 0;
- }
- return true;
-}
-
-/**
- * Return true if the attribute should be copied to poly curves.
- */
-static bool interpolate_attribute_to_poly_curve(const AttributeIDRef &attribute_id)
-{
- static const Set<StringRef> no_interpolation{{
- "handle_type_left",
- "handle_type_right",
- "handle_position_right",
- "handle_position_left",
- "nurbs_weight",
- }};
- return !(attribute_id.is_named() && no_interpolation.contains(attribute_id.name()));
-}
-
-/**
- * Retrieve spans from source and result attributes.
- */
-static void retrieve_attribute_spans(const Span<AttributeIDRef> ids,
- const CurveComponent &src_component,
- CurveComponent &dst_component,
- Vector<GSpan> &src,
- Vector<GMutableSpan> &dst,
- Vector<OutputAttribute> &dst_attributes)
-{
- for (const int i : ids.index_range()) {
- GVArray src_attribute = src_component.attribute_try_get_for_read(ids[i], ATTR_DOMAIN_POINT);
- BLI_assert(src_attribute);
- src.append(src_attribute.get_internal_span());
-
- const CustomDataType data_type = bke::cpp_type_to_custom_data_type(src_attribute.type());
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
- ids[i], ATTR_DOMAIN_POINT, data_type);
- dst.append(dst_attribute.as_span());
- dst_attributes.append(std::move(dst_attribute));
- }
-}
-
-struct AttributesForInterpolation : NonCopyable, NonMovable {
- Vector<GSpan> src;
- Vector<GMutableSpan> dst;
-
- Vector<OutputAttribute> dst_attributes;
-
- Vector<GSpan> src_no_interpolation;
- Vector<GMutableSpan> dst_no_interpolation;
-};
-
-/**
- * Gather a set of all generic attribute IDs to copy to the result curves.
- */
-static void gather_point_attributes_to_interpolate(const CurveComponent &src_component,
- CurveComponent &dst_component,
- AttributesForInterpolation &result)
-{
- const Curves &dst_curves_id = *dst_component.get_for_read();
- const bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id.geometry);
-
- VectorSet<AttributeIDRef> ids;
- VectorSet<AttributeIDRef> ids_no_interpolation;
- src_component.attribute_foreach(
- [&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
- if (meta_data.domain != ATTR_DOMAIN_POINT) {
- return true;
- }
- if (!interpolate_attribute_to_curves(id, dst_curves.curve_type_counts())) {
- return true;
- }
- if (interpolate_attribute_to_poly_curve(id)) {
- ids.add_new(id);
- }
- else {
- ids_no_interpolation.add_new(id);
- }
- return true;
- });
-
- /* Position is handled differently since it has non-generic interpolation for Bezier
- * curves and because the evaluated positions are cached for each evaluated point. */
- ids.remove_contained("position");
-
- retrieve_attribute_spans(
- ids, src_component, dst_component, result.src, result.dst, result.dst_attributes);
-
- /* Attributes that aren't interpolated like Bezier handles still have to be be copied
- * to the result when there are any unselected curves of the corresponding type. */
- retrieve_attribute_spans(ids_no_interpolation,
- src_component,
- dst_component,
- result.src_no_interpolation,
- result.dst_no_interpolation,
- result.dst_attributes);
-}
-
-/**
- * Copy the provided point attribute values between all curves in the #curve_ranges index
- * ranges, assuming that all curves are the same size in #src_curves and #dst_curves.
- */
-template<typename T>
-static void copy_between_curves(const bke::CurvesGeometry &src_curves,
- const bke::CurvesGeometry &dst_curves,
- const Span<IndexRange> curve_ranges,
- const Span<T> src,
- const MutableSpan<T> dst)
-{
- threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) {
- for (const IndexRange range : curve_ranges.slice(range)) {
- const IndexRange src_points = src_curves.points_for_curves(range);
- const IndexRange dst_points = dst_curves.points_for_curves(range);
- /* The arrays might be large, so a threaded copy might make sense here too. */
- dst.slice(dst_points).copy_from(src.slice(src_points));
- }
- });
-}
-static void copy_between_curves(const bke::CurvesGeometry &src_curves,
- const bke::CurvesGeometry &dst_curves,
- const Span<IndexRange> unselected_ranges,
- const GSpan src,
- const GMutableSpan dst)
-{
- attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
- using T = decltype(dummy);
- copy_between_curves(src_curves, dst_curves, unselected_ranges, src.typed<T>(), dst.typed<T>());
- });
-}
-
-/**
- * Copy the size of every curve in #curve_ranges to the corresponding index in #counts.
- */
-static void fill_curve_counts(const bke::CurvesGeometry &src_curves,
- const Span<IndexRange> curve_ranges,
- MutableSpan<int> counts)
-{
- threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange ranges_range) {
- for (const IndexRange curves_range : curve_ranges.slice(ranges_range)) {
- for (const int i : curves_range) {
- counts[i] = src_curves.points_for_curve(i).size();
- }
- }
- });
-}
-
-/**
- * Turn an array of sizes into the offset at each index including all previous sizes.
- */
-static void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets)
-{
- int total = 0;
- for (const int i : counts_to_offsets.index_range().drop_back(1)) {
- const int count = counts_to_offsets[i];
- BLI_assert(count > 0);
- counts_to_offsets[i] = total;
- total += count;
- }
- counts_to_offsets.last() = total;
-}
-
-/**
- * Create new curves where the selected curves have been resampled with a number of uniform-length
- * samples defined by the count field. Interpolate attributes to the result, with an accuracy that
- * depends on the curve's resolution parameter.
- *
- * \warning The values provided by the #count_field must be 1 or greater.
- * \warning Curves with no evaluated points must not be selected.
- */
-static Curves *resample_to_uniform_count(const CurveComponent &src_component,
- const Field<bool> &selection_field,
- const Field<int> &count_field)
-{
- const Curves &src_curves_id = *src_component.get_for_read();
- const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
-
- /* Create the new curves without any points and evaluate the final count directly
- * into the offsets array, in order to be accumulated into offsets later. */
- Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num());
- bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry);
- CurveComponent dst_component;
- dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable);
- /* Directly copy curve attributes, since they stay the same (except for curve types). */
- CustomData_copy(&src_curves.curve_data,
- &dst_curves.curve_data,
- CD_MASK_ALL,
- CD_DUPLICATE,
- src_curves.curves_num());
- MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
-
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
- fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
- evaluator.set_selection(selection_field);
- evaluator.add_with_destination(count_field, dst_offsets);
- evaluator.evaluate();
- const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
- const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
- src_curves.curves_range(), nullptr);
-
- /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */
- fill_curve_counts(src_curves, unselected_ranges, dst_offsets);
- accumulate_counts_to_offsets(dst_offsets);
- dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
-
- /* All resampled curves are poly curves. */
- dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY);
-
- VArray<bool> curves_cyclic = src_curves.cyclic();
- VArray<int8_t> curve_types = src_curves.curve_types();
- Span<float3> evaluated_positions = src_curves.evaluated_positions();
- MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
-
- AttributesForInterpolation attributes;
- gather_point_attributes_to_interpolate(src_component, dst_component, attributes);
-
- src_curves.ensure_evaluated_lengths();
-
- /* Sampling arbitrary attributes works by first interpolating them to the curve's standard
- * "evaluated points" and then interpolating that result with the uniform samples. This is
- * potentially wasteful when down-sampling a curve to many fewer points. There are two possible
- * solutions: only sample the necessary points for interpolation, or first sample curve
- * parameter/segment indices and evaluate the curve directly. */
- Array<int> sample_indices(dst_curves.points_num());
- Array<float> sample_factors(dst_curves.points_num());
-
- /* Use a "for each group of curves: for each attribute: for each curve" pattern to work on
- * smaller sections of data that ideally fit into CPU cache better than simply one attribute at a
- * time or one curve at a time. */
- threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) {
- const IndexMask sliced_selection = selection.slice(selection_range);
-
- Vector<std::byte> evaluated_buffer;
-
- /* Gather uniform samples based on the accumulated lengths of the original curve. */
- for (const int i_curve : sliced_selection) {
- const bool cyclic = curves_cyclic[i_curve];
- const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
- length_parameterize::create_uniform_samples(
- src_curves.evaluated_lengths_for_curve(i_curve, cyclic),
- curves_cyclic[i_curve],
- sample_indices.as_mutable_span().slice(dst_points),
- sample_factors.as_mutable_span().slice(dst_points));
- }
-
- /* For every attribute, evaluate attributes from every curve in the range in the original
- * curve's "evaluated points", then use linear interpolation to sample to the result. */
- for (const int i_attribute : attributes.dst.index_range()) {
- attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) {
- using T = decltype(dummy);
- Span<T> src = attributes.src[i_attribute].typed<T>();
- MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>();
-
- for (const int i_curve : sliced_selection) {
- const IndexRange src_points = src_curves.points_for_curve(i_curve);
- const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
-
- if (curve_types[i_curve] == CURVE_TYPE_POLY) {
- length_parameterize::linear_interpolation(src.slice(src_points),
- sample_indices.as_span().slice(dst_points),
- sample_factors.as_span().slice(dst_points),
- dst.slice(dst_points));
- }
- else {
- const int evaluated_size = src_curves.evaluated_points_for_curve(i_curve).size();
- evaluated_buffer.clear();
- evaluated_buffer.resize(sizeof(T) * evaluated_size);
- MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>();
- src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated);
-
- length_parameterize::linear_interpolation(evaluated.as_span(),
- sample_indices.as_span().slice(dst_points),
- sample_factors.as_span().slice(dst_points),
- dst.slice(dst_points));
- }
- }
- });
- }
-
- /* Interpolate the evaluated positions to the resampled curves. */
- for (const int i_curve : sliced_selection) {
- const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve);
- const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
- length_parameterize::linear_interpolation(evaluated_positions.slice(src_points),
- sample_indices.as_span().slice(dst_points),
- sample_factors.as_span().slice(dst_points),
- dst_positions.slice(dst_points));
- }
-
- /* Fill the default value for non-interpolating attributes that still must be copied. */
- for (GMutableSpan dst : attributes.dst_no_interpolation) {
- for (const int i_curve : sliced_selection) {
- const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
- dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size());
- }
- }
- });
-
- /* Any attribute data from unselected curve points can be directly copied. */
- for (const int i : attributes.src.index_range()) {
- copy_between_curves(
- src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
- }
- for (const int i : attributes.src_no_interpolation.index_range()) {
- copy_between_curves(src_curves,
- dst_curves,
- unselected_ranges,
- attributes.src_no_interpolation[i],
- attributes.dst_no_interpolation[i]);
- }
-
- /* Copy positions for unselected curves. */
- Span<float3> src_positions = src_curves.positions();
- copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions);
-
- for (OutputAttribute &attribute : attributes.dst_attributes) {
- attribute.save();
- }
-
- return dst_curves_id;
-}
-
-/**
- * Evaluate each selected curve to its implicit evaluated points.
- *
- * \warning Curves with no evaluated points must not be selected.
- */
-static Curves *resample_to_evaluated(const CurveComponent &src_component,
- const Field<bool> &selection_field)
-{
- const Curves &src_curves_id = *src_component.get_for_read();
- const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
-
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
- fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
- evaluator.set_selection(selection_field);
- evaluator.evaluate();
- const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
- const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
- src_curves.curves_range(), nullptr);
-
- Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num());
- bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry);
- CurveComponent dst_component;
- dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable);
- /* Directly copy curve attributes, since they stay the same (except for curve types). */
- CustomData_copy(&src_curves.curve_data,
- &dst_curves.curve_data,
- CD_MASK_ALL,
- CD_DUPLICATE,
- src_curves.curves_num());
- /* All resampled curves are poly curves. */
- dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY);
- MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
-
- src_curves.ensure_evaluated_offsets();
- threading::parallel_for(selection.index_range(), 4096, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- dst_offsets[i] = src_curves.evaluated_points_for_curve(i).size();
- }
- });
- fill_curve_counts(src_curves, unselected_ranges, dst_offsets);
- accumulate_counts_to_offsets(dst_offsets);
-
- dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
-
- /* Create the correct number of uniform-length samples for every selected curve. */
- Span<float3> evaluated_positions = src_curves.evaluated_positions();
- MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
-
- AttributesForInterpolation attributes;
- gather_point_attributes_to_interpolate(src_component, dst_component, attributes);
-
- threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) {
- const IndexMask sliced_selection = selection.slice(selection_range);
-
- /* Evaluate generic point attributes directly to the result attributes. */
- for (const int i_attribute : attributes.dst.index_range()) {
- attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) {
- using T = decltype(dummy);
- Span<T> src = attributes.src[i_attribute].typed<T>();
- MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>();
-
- for (const int i_curve : sliced_selection) {
- const IndexRange src_points = src_curves.points_for_curve(i_curve);
- const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
- src_curves.interpolate_to_evaluated(
- i_curve, src.slice(src_points), dst.slice(dst_points));
- }
- });
- }
-
- /* Copy the evaluated positions to the selected curves. */
- for (const int i_curve : sliced_selection) {
- const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve);
- const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
- dst_positions.slice(dst_points).copy_from(evaluated_positions.slice(src_points));
- }
-
- /* Fill the default value for non-interpolating attributes that still must be copied. */
- for (GMutableSpan dst : attributes.dst_no_interpolation) {
- for (const int i_curve : sliced_selection) {
- const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
- dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size());
- }
- }
- });
-
- /* Any attribute data from unselected curve points can be directly copied. */
- for (const int i : attributes.src.index_range()) {
- copy_between_curves(
- src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
- }
- for (const int i : attributes.src_no_interpolation.index_range()) {
- copy_between_curves(src_curves,
- dst_curves,
- unselected_ranges,
- attributes.src_no_interpolation[i],
- attributes.dst_no_interpolation[i]);
- }
-
- /* Copy positions for unselected curves. */
- Span<float3> src_positions = src_curves.positions();
- copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions);
-
- for (OutputAttribute &attribute : attributes.dst_attributes) {
- attribute.save();
- }
-
- return dst_curves_id;
-}
-
-/**
- * Create a resampled curve point count field for both "uniform" options.
- * The complexity is handled here in order to make the actual resampling functions simpler.
- */
-static Field<int> get_curve_count_field(GeoNodeExecParams params,
- const GeometryNodeCurveResampleMode mode)
-{
- if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) {
- static fn::CustomMF_SI_SO<int, int> max_one_fn(
- "Clamp Above One",
- [](int value) { return std::max(1, value); },
- fn::CustomMF_presets::AllSpanOrSingle());
- auto clamp_op = std::make_shared<FieldOperation>(
- FieldOperation(max_one_fn, {Field<int>(params.extract_input<Field<int>>("Count"))}));
-
- return Field<int>(std::move(clamp_op));
- }
-
- if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) {
- static fn::CustomMF_SI_SI_SO<float, float, int> get_count_fn(
- "Length Input to Count",
- [](const float curve_length, const float sample_length) {
- /* Find the number of sampled segments by dividing the total length by
- * the sample length. Then there is one more sampled point than segment. */
- const int count = int(curve_length / sample_length) + 1;
- return std::max(1, count);
- },
- fn::CustomMF_presets::AllSpanOrSingle());
-
- auto get_count_op = std::make_shared<FieldOperation>(
- FieldOperation(get_count_fn,
- {Field<float>(std::make_shared<SplineLengthFieldInput>()),
- params.extract_input<Field<float>>("Length")}));
-
- return Field<int>(std::move(get_count_op));
- }
-
- BLI_assert_unreachable();
- return {};
-}
-
-/**
- * Create a selection field that removes curves without any evaluated points (invalid NURBS curves)
- * from the original selection provided to the node. This is here to simplify the sampling actual
- * resampling code.
- */
-static Field<bool> get_selection_field(GeoNodeExecParams params)
-{
- static fn::CustomMF_SI_SI_SO<bool, int, bool> get_selection_fn(
- "Create Curve Selection",
- [](const bool orig_selection, const int evaluated_points_num) {
- return orig_selection && evaluated_points_num > 1;
- },
- fn::CustomMF_presets::AllSpanOrSingle());
-
- auto selection_op = std::make_shared<FieldOperation>(
- FieldOperation(get_selection_fn,
- {params.extract_input<Field<bool>>("Selection"),
- Field<int>(std::make_shared<EvaluatedCountFieldInput>())}));
-
- return Field<bool>(std::move(selection_op));
-}
-
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
@@ -613,35 +58,38 @@ static void node_geo_exec(GeoNodeExecParams params)
const NodeGeometryCurveResample &storage = node_storage(params.node());
const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode;
- const Field<bool> selection = get_selection_field(params);
+ const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
switch (mode) {
- case GEO_NODE_CURVE_RESAMPLE_COUNT:
+ case GEO_NODE_CURVE_RESAMPLE_COUNT: {
+ Field<int> count = params.extract_input<Field<int>>("Count");
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
+ if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) {
+ if (!component->is_empty()) {
+ geometry.replace_curves(geometry::resample_to_count(*component, selection, count));
+ }
+ }
+ });
+ break;
+ }
case GEO_NODE_CURVE_RESAMPLE_LENGTH: {
- Field<int> count = get_curve_count_field(params, mode);
-
- geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_curves()) {
- return;
+ Field<int> length = params.extract_input<Field<float>>("Length");
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
+ if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) {
+ if (!component->is_empty()) {
+ geometry.replace_curves(geometry::resample_to_length(*component, selection, length));
+ }
}
-
- Curves *result = resample_to_uniform_count(
- *geometry_set.get_component_for_read<CurveComponent>(), selection, count);
-
- geometry_set.replace_curves(result);
});
break;
}
case GEO_NODE_CURVE_RESAMPLE_EVALUATED:
- geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_curves()) {
- return;
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
+ if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) {
+ if (!component->is_empty()) {
+ geometry.replace_curves(geometry::resample_to_evaluated(*component, selection));
+ }
}
-
- Curves *result = resample_to_evaluated(
- *geometry_set.get_component_for_read<CurveComponent>(), selection);
-
- geometry_set.replace_curves(result);
});
break;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
index de29735bd2d..64a174df599 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
@@ -27,9 +27,9 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
+ const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE);
- fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ fn::FieldEvaluator selection_evaluator{field_context, domain_num};
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc
index 5d97720a4f8..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..b45a0d6c81a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
@@ -90,7 +90,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams &params,
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 &params,
int offset = 0;
for (const int i : IndexRange(size)) {
offsets[i] = offset;
- if (splines[i]->evaluated_points_size() > 0) {
+ if (splines[i]->evaluated_points_num() > 0) {
offset += splines[i]->length() / resolution + 1;
}
}
@@ -240,18 +240,18 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines,
for (const int i : range) {
const Spline &spline = *splines[i];
const int offset = offsets[i];
- const int size = offsets[i + 1] - offsets[i];
- if (size == 0) {
+ const int num = offsets[i + 1] - offsets[i];
+ if (num == 0) {
continue;
}
- const Array<float> uniform_samples = spline.sample_uniform_index_factors(size);
+ const Array<float> uniform_samples = spline.sample_uniform_index_factors(num);
spline.sample_with_index_factors<float3>(
- spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, size));
+ spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, num));
spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()),
uniform_samples,
- data.radii.slice(offset, size));
+ data.radii.slice(offset, num));
for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) {
const AttributeIDRef attribute_id = item.key;
@@ -260,14 +260,13 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines,
BLI_assert(spline.attributes.get_for_read(attribute_id));
GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
- spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline_span),
- uniform_samples,
- dst.slice(offset, size));
+ spline.sample_with_index_factors(
+ spline.interpolate_to_evaluated(spline_span), uniform_samples, dst.slice(offset, num));
}
if (!data.tangents.is_empty()) {
Span<float3> src_tangents = spline.evaluated_tangents();
- MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, size);
+ MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, num);
spline.sample_with_index_factors<float3>(src_tangents, uniform_samples, sampled_tangents);
for (float3 &vector : sampled_tangents) {
vector = math::normalize(vector);
@@ -276,7 +275,7 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines,
if (!data.normals.is_empty()) {
Span<float3> src_normals = spline.evaluated_normals();
- MutableSpan<float3> sampled_normals = data.normals.slice(offset, size);
+ MutableSpan<float3> sampled_normals = data.normals.slice(offset, num);
spline.sample_with_index_factors<float3>(src_normals, uniform_samples, sampled_normals);
for (float3 &vector : sampled_normals) {
vector = math::normalize(vector);
@@ -298,8 +297,8 @@ static void copy_spline_domain_attributes(const CurveEval &curve,
for (const int i : curve.splines().index_range()) {
const int offset = offsets[i];
- const int size = offsets[i + 1] - offsets[i];
- type.fill_assign_n(curve_attribute[i], dst[offset], size);
+ const int num = offsets[i + 1] - offsets[i];
+ type.fill_assign_n(curve_attribute[i], dst[offset], num);
}
return true;
@@ -329,13 +328,13 @@ static void node_geo_exec(GeoNodeExecParams params)
curve->assert_valid_point_attributes();
const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines);
- const int total_size = offsets.last();
- if (total_size == 0) {
+ const int total_num = offsets.last();
+ if (total_num == 0) {
geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
return;
}
- geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_size));
+ geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_num));
PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>();
ResultAttributes point_attributes = create_attributes_for_transfer(
points, *curve, attribute_outputs);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
index df360818313..c993a3d305d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
@@ -117,10 +117,10 @@ struct TrimLocation {
};
template<typename T>
-static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int size)
+static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int num)
{
- BLI_assert(start_index + size - 1 <= data.size());
- memmove(data.data(), &data[start_index], sizeof(T) * size);
+ BLI_assert(start_index + num - 1 <= data.size());
+ memmove(data.data(), &data[start_index], sizeof(T) * num);
}
/* Shift slice to start of span and modifies start and end data. */
@@ -129,17 +129,17 @@ static void linear_trim_data(const TrimLocation &start,
const TrimLocation &end,
MutableSpan<T> data)
{
- const int size = end.right_index - start.left_index + 1;
+ const int num = end.right_index - start.left_index + 1;
if (start.left_index > 0) {
- shift_slice_to_start<T>(data, start.left_index, size);
+ shift_slice_to_start<T>(data, start.left_index, num);
}
const T start_data = mix2<T>(start.factor, data.first(), data[1]);
- const T end_data = mix2<T>(end.factor, data[size - 2], data[size - 1]);
+ const T end_data = mix2<T>(end.factor, data[num - 2], data[num - 1]);
data.first() = start_data;
- data[size - 1] = end_data;
+ data[num - 1] = end_data;
}
/**
@@ -152,12 +152,12 @@ static void linear_trim_to_output_data(const TrimLocation &start,
Span<T> src,
MutableSpan<T> dst)
{
- const int size = end.right_index - start.left_index + 1;
+ const int num = end.right_index - start.left_index + 1;
const T start_data = mix2<T>(start.factor, src[start.left_index], src[start.right_index]);
const T end_data = mix2<T>(end.factor, src[end.left_index], src[end.right_index]);
- dst.copy_from(src.slice(start.left_index, size));
+ dst.copy_from(src.slice(start.left_index, num));
dst.first() = start_data;
dst.last() = end_data;
}
@@ -175,8 +175,8 @@ static TrimLocation lookup_control_point_position(const Spline::LookupResult &lo
const int right = left == (spline.size() - 1) ? 0 : left + 1;
const float offset_in_segment = lookup.evaluated_index + lookup.factor - offsets[left];
- const int segment_eval_size = offsets[left + 1] - offsets[left];
- const float factor = std::clamp(offset_in_segment / segment_eval_size, 0.0f, 1.0f);
+ const int segment_eval_num = offsets[left + 1] - offsets[left];
+ const float factor = std::clamp(offset_in_segment / segment_eval_num, 0.0f, 1.0f);
return {left, right, factor};
}
@@ -191,7 +191,7 @@ static void trim_poly_spline(Spline &spline,
const TrimLocation end = {
end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor};
- const int size = end.right_index - start.left_index + 1;
+ const int num = end.right_index - start.left_index + 1;
linear_trim_data<float3>(start, end, spline.positions());
linear_trim_data<float>(start, end, spline.radii());
@@ -209,7 +209,7 @@ static void trim_poly_spline(Spline &spline,
},
ATTR_DOMAIN_POINT);
- spline.resize(size);
+ spline.resize(num);
}
/**
@@ -225,11 +225,11 @@ static PolySpline trim_nurbs_spline(const Spline &spline,
const TrimLocation end = {
end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor};
- const int size = end.right_index - start.left_index + 1;
+ const int num = end.right_index - start.left_index + 1;
/* Create poly spline and copy trimmed data to it. */
PolySpline new_spline;
- new_spline.resize(size);
+ new_spline.resize(num);
/* Copy generic attribute data. */
spline.attributes.foreach_attribute(
@@ -283,7 +283,7 @@ static void trim_bezier_spline(Spline &spline,
const Span<int> control_offsets = bezier_spline.control_point_offsets();
/* The number of control points in the resulting spline. */
- const int size = end.right_index - start.left_index + 1;
+ const int num = end.right_index - start.left_index + 1;
/* Trim the spline attributes. Done before end.factor recalculation as it needs
* the original end.factor value. */
@@ -301,10 +301,10 @@ static void trim_bezier_spline(Spline &spline,
},
ATTR_DOMAIN_POINT);
- /* Recalculate end.factor if the size is two, because the adjustment in the
+ /* Recalculate end.factor if the `num` is two, because the adjustment in the
* position of the control point of the spline to the left of the new end point will change the
* factor between them. */
- if (size == 2) {
+ if (num == 2) {
if (start_lookup.factor == 1.0f) {
end.factor = 0.0f;
}
@@ -328,38 +328,38 @@ static void trim_bezier_spline(Spline &spline,
const BezierSpline::InsertResult end_point = bezier_spline.calculate_segment_insertion(
end.left_index, end.right_index, end.factor);
- /* If size is two, then the start point right handle needs to change to reflect the end point
+ /* If `num` is two, then the start point right handle needs to change to reflect the end point
* previous handle update. */
- if (size == 2) {
+ if (num == 2) {
start_point.right_handle = end_point.handle_prev;
}
/* Shift control point position data to start at beginning of array. */
if (start.left_index > 0) {
- shift_slice_to_start(bezier_spline.positions(), start.left_index, size);
- shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, size);
- shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, size);
+ shift_slice_to_start(bezier_spline.positions(), start.left_index, num);
+ shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, num);
+ shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, num);
}
bezier_spline.positions().first() = start_point.position;
- bezier_spline.positions()[size - 1] = end_point.position;
+ bezier_spline.positions()[num - 1] = end_point.position;
bezier_spline.handle_positions_left().first() = start_point.left_handle;
- bezier_spline.handle_positions_left()[size - 1] = end_point.left_handle;
+ bezier_spline.handle_positions_left()[num - 1] = end_point.left_handle;
bezier_spline.handle_positions_right().first() = start_point.right_handle;
- bezier_spline.handle_positions_right()[size - 1] = end_point.right_handle;
+ bezier_spline.handle_positions_right()[num - 1] = end_point.right_handle;
/* If there is at least one control point between the endpoints, update the control
* point handle to the right of the start point and to the left of the end point. */
- if (size > 2) {
+ if (num > 2) {
bezier_spline.handle_positions_left()[start.right_index - start.left_index] =
start_point.handle_next;
bezier_spline.handle_positions_right()[end.left_index - start.left_index] =
end_point.handle_prev;
}
- bezier_spline.resize(size);
+ bezier_spline.resize(num);
}
static void trim_spline(SplinePtr &spline,
@@ -506,9 +506,9 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
+ const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE);
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ fn::FieldEvaluator evaluator{field_context, domain_num};
evaluator.add(start_field);
evaluator.add(end_field);
evaluator.evaluate();
@@ -527,7 +527,7 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
continue;
}
- if (spline->evaluated_edges_size() == 0) {
+ if (spline->evaluated_edges_num() == 0) {
continue;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
index 8a0c900fbde..99edc4d298c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
@@ -470,7 +470,7 @@ static void separate_curve_selection(GeometrySet &geometry_set,
GeometryComponentFieldContext field_context{src_component, selection_domain};
fn::FieldEvaluator selection_evaluator{field_context,
- src_component.attribute_domain_size(selection_domain)};
+ src_component.attribute_domain_num(selection_domain)};
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
@@ -493,7 +493,7 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set,
GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT};
fn::FieldEvaluator selection_evaluator{field_context,
- src_points.attribute_domain_size(ATTR_DOMAIN_POINT)};
+ src_points.attribute_domain_num(ATTR_DOMAIN_POINT)};
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
@@ -526,8 +526,8 @@ static void separate_instance_selection(GeometrySet &geometry_set,
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
- const int domain_size = instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE);
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ const int domain_num = instances.attribute_domain_num(ATTR_DOMAIN_INSTANCE);
+ fn::FieldEvaluator evaluator{field_context, domain_num};
evaluator.add(selection_field);
evaluator.evaluate();
const VArray_Span<bool> &selection = evaluator.get_evaluated<bool>(0);
@@ -1238,7 +1238,7 @@ static void separate_mesh_selection(GeometrySet &geometry_set,
GeometryComponentFieldContext field_context{src_component, selection_domain};
fn::FieldEvaluator selection_evaluator{field_context,
- src_component.attribute_domain_size(selection_domain)};
+ src_component.attribute_domain_num(selection_domain)};
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
index d9f29d1ef1c..c242cfd5cf3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
@@ -398,11 +398,11 @@ static Array<float> calc_full_density_factors_with_selection(const MeshComponent
{
const AttributeDomain attribute_domain = ATTR_DOMAIN_CORNER;
GeometryComponentFieldContext field_context{component, attribute_domain};
- const int domain_size = component.attribute_domain_size(attribute_domain);
+ const int domain_num = component.attribute_domain_num(attribute_domain);
- Array<float> densities(domain_size, 0.0f);
+ Array<float> densities(domain_num, 0.0f);
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ fn::FieldEvaluator evaluator{field_context, domain_num};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(density_field, densities.as_mutable_span());
evaluator.evaluate();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
index 1b26cfe31fe..ee3de995cb1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
@@ -351,19 +351,19 @@ static void duplicate_curves(GeometrySet &geometry_set,
Array<int> curve_offsets(selection.size() + 1);
Array<int> point_offsets(selection.size() + 1);
- int dst_curves_size = 0;
- int dst_points_size = 0;
+ int dst_curves_num = 0;
+ int dst_points_num = 0;
for (const int i_curve : selection.index_range()) {
const int count = std::max(counts[selection[i_curve]], 0);
- curve_offsets[i_curve] = dst_curves_size;
- point_offsets[i_curve] = dst_points_size;
- dst_curves_size += count;
- dst_points_size += count * curves.points_for_curve(selection[i_curve]).size();
+ curve_offsets[i_curve] = dst_curves_num;
+ point_offsets[i_curve] = dst_points_num;
+ dst_curves_num += count;
+ dst_points_num += count * curves.points_for_curve(selection[i_curve]).size();
}
- curve_offsets.last() = dst_curves_size;
- point_offsets.last() = dst_points_size;
+ curve_offsets.last() = dst_curves_num;
+ point_offsets.last() = dst_points_num;
- Curves *new_curves_id = bke::curves_new_nomain(dst_points_size, dst_curves_size);
+ Curves *new_curves_id = bke::curves_new_nomain(dst_points_num, dst_curves_num);
bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry);
MutableSpan<int> all_dst_offsets = new_curves.offsets_for_write();
@@ -379,7 +379,7 @@ static void duplicate_curves(GeometrySet &geometry_set,
}
}
});
- all_dst_offsets.last() = dst_points_size;
+ all_dst_offsets.last() = dst_points_num;
CurveComponent dst_component;
dst_component.replace(new_curves_id, GeometryOwnershipType::Editable);
@@ -807,7 +807,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
- const int dst_size = offsets.last();
+ const int dst_num = offsets.last();
Array<int> point_to_curve_map(src_curves.points_num());
threading::parallel_for(src_curves.curves_range(), 1024, [&](const IndexRange range) {
@@ -817,13 +817,13 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
}
});
- Curves *new_curves_id = bke::curves_new_nomain(dst_size, dst_size);
+ Curves *new_curves_id = bke::curves_new_nomain(dst_num, dst_num);
bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry);
MutableSpan<int> new_curve_offsets = new_curves.offsets_for_write();
for (const int i : new_curves.curves_range()) {
new_curve_offsets[i] = i;
}
- new_curve_offsets.last() = dst_size;
+ new_curve_offsets.last() = dst_num;
CurveComponent dst_component;
dst_component.replace(new_curves_id, GeometryOwnershipType::Editable);
@@ -940,10 +940,10 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set,
{
const PointCloudComponent &src_points =
*geometry_set.get_component_for_read<PointCloudComponent>();
- const int point_size = src_points.attribute_domain_size(ATTR_DOMAIN_POINT);
+ const int point_num = src_points.attribute_domain_num(ATTR_DOMAIN_POINT);
GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT};
- FieldEvaluator evaluator{field_context, point_size};
+ FieldEvaluator evaluator{field_context, point_num};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
evaluator.evaluate();
@@ -1026,7 +1026,7 @@ static void duplicate_instances(GeometrySet &geometry_set,
*geometry_set.get_component_for_read<InstancesComponent>();
GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE};
- FieldEvaluator evaluator{field_context, src_instances.instances_amount()};
+ FieldEvaluator evaluator{field_context, src_instances.instances_num()};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
evaluator.evaluate();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
index 84acab47661..94fbec66bfe 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
@@ -57,8 +57,8 @@ static void node_geo_exec(GeoNodeExecParams params)
const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
- const int domain_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
- fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ const int domain_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE);
+ fn::FieldEvaluator selection_evaluator{field_context, domain_num};
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
index 77be98f169e..bf956f3b83d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
@@ -91,7 +91,7 @@ class FieldAtIndex final : public GeometryFieldInput {
{
const GeometryComponentFieldContext value_field_context{component, value_field_domain_};
FieldEvaluator value_evaluator{value_field_context,
- component.attribute_domain_size(value_field_domain_)};
+ component.attribute_domain_num(value_field_domain_)};
value_evaluator.add(value_field_);
value_evaluator.evaluate();
const GVArray &values = value_evaluator.get_evaluated(0);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
index 34c3169065c..0484017cf3b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
@@ -22,11 +22,11 @@ static void node_declare(NodeDeclarationBuilder &b)
static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selection_field)
{
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
- if (domain_size == 0) {
+ const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE);
+ if (domain_num == 0) {
return;
}
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ fn::FieldEvaluator evaluator{field_context, domain_num};
evaluator.add(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
index d39291a2a7e..8e3a9b6769d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
@@ -101,8 +101,7 @@ class IslandCountFieldInput final : public GeometryFieldInput {
island_list.add(root);
}
- return VArray<int>::ForSingle(island_list.size(),
- mesh_component.attribute_domain_size(domain));
+ return VArray<int>::ForSingle(island_list.size(), mesh_component.attribute_domain_num(domain));
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
index 6c24f86b63b..84d773ff8eb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
@@ -4,71 +4,6 @@
#include "BKE_curves.hh"
-namespace blender::nodes {
-
-/* --------------------------------------------------------------------
- * Spline Length
- */
-
-static VArray<float> construct_spline_length_gvarray(const CurveComponent &component,
- const AttributeDomain domain)
-{
- if (!component.has_curves()) {
- return {};
- }
- const Curves &curves_id = *component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
-
- curves.ensure_evaluated_lengths();
-
- VArray<bool> cyclic = curves.cyclic();
- VArray<float> lengths = VArray<float>::ForFunc(
- curves.curves_num(), [&curves, cyclic = std::move(cyclic)](int64_t index) {
- return curves.evaluated_length_total_for_curve(index, cyclic[index]);
- });
-
- if (domain == ATTR_DOMAIN_CURVE) {
- return lengths;
- }
-
- if (domain == ATTR_DOMAIN_POINT) {
- return component.attribute_try_adapt_domain<float>(
- std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
- }
-
- return {};
-}
-
-SplineLengthFieldInput::SplineLengthFieldInput()
- : GeometryFieldInput(CPPType::get<float>(), "Spline Length node")
-{
- category_ = Category::Generated;
-}
-
-GVArray SplineLengthFieldInput::get_varray_for_context(const GeometryComponent &component,
- const AttributeDomain domain,
- IndexMask UNUSED(mask)) const
-{
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- return construct_spline_length_gvarray(curve_component, domain);
- }
- return {};
-}
-
-uint64_t SplineLengthFieldInput::hash() const
-{
- /* Some random constant hash. */
- return 3549623580;
-}
-
-bool SplineLengthFieldInput::is_equal_to(const fn::FieldNode &other) const
-{
- return dynamic_cast<const SplineLengthFieldInput *>(&other) != nullptr;
-}
-
-} // namespace blender::nodes
-
namespace blender::nodes::node_geo_input_spline_length_cc {
static void node_declare(NodeDeclarationBuilder &b)
@@ -81,8 +16,8 @@ static void node_declare(NodeDeclarationBuilder &b)
* Spline Count
*/
-static VArray<int> construct_spline_count_gvarray(const CurveComponent &component,
- const AttributeDomain domain)
+static VArray<int> construct_curve_point_count_gvarray(const CurveComponent &component,
+ const AttributeDomain domain)
{
if (!component.has_curves()) {
return {};
@@ -117,7 +52,7 @@ class SplineCountFieldInput final : public GeometryFieldInput {
{
if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- return construct_spline_count_gvarray(curve_component, domain);
+ return construct_curve_point_count_gvarray(curve_component, domain);
}
return {};
}
@@ -136,7 +71,7 @@ class SplineCountFieldInput final : public GeometryFieldInput {
static void node_geo_exec(GeoNodeExecParams params)
{
- Field<float> spline_length_field{std::make_shared<SplineLengthFieldInput>()};
+ Field<float> spline_length_field{std::make_shared<bke::CurveLengthFieldInput>()};
Field<int> spline_count_field{std::make_shared<SplineCountFieldInput>()};
params.set_output("Length", std::move(spline_length_field));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
index 61f719ade4e..12582f9e9c6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
@@ -50,7 +50,7 @@ static void add_instances_from_component(
const Map<AttributeIDRef, AttributeKind> &attributes_to_propagate)
{
const AttributeDomain domain = ATTR_DOMAIN_POINT;
- const int domain_size = src_component.attribute_domain_size(domain);
+ const int domain_num = src_component.attribute_domain_num(domain);
VArray<bool> pick_instance;
VArray<int> indices;
@@ -59,7 +59,7 @@ static void add_instances_from_component(
GeometryComponentFieldContext field_context{src_component, domain};
const Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ fn::FieldEvaluator evaluator{field_context, domain_num};
evaluator.set_selection(selection_field);
/* The evaluator could use the component's stable IDs as a destination directly, but only the
* selected indices should be copied. */
@@ -73,7 +73,7 @@ static void add_instances_from_component(
/* The initial size of the component might be non-zero when this function is called for multiple
* component types. */
- const int start_len = dst_component.instances_amount();
+ const int start_len = dst_component.instances_num();
const int select_len = selection.index_range().size();
dst_component.resize(start_len + select_len);
@@ -119,12 +119,12 @@ static void add_instances_from_component(
const bool use_individual_instance = pick_instance[i];
if (use_individual_instance) {
if (src_instances != nullptr) {
- const int src_instances_amount = src_instances->instances_amount();
+ const int src_instances_num = src_instances->instances_num();
const int original_index = indices[i];
/* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1
* refers to the last element. */
- const int index = mod_i(original_index, std::max(src_instances_amount, 1));
- if (index < src_instances_amount) {
+ const int index = mod_i(original_index, std::max(src_instances_num, 1));
+ if (index < src_instances_num) {
/* Get the reference to the source instance. */
const int src_handle = src_instances->instance_reference_handles()[index];
dst_handle = handle_mapping[src_handle];
diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
index e35f152ce49..2126a5cc329 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
@@ -40,9 +40,9 @@ static void convert_instances_to_points(GeometrySet &geometry_set,
const InstancesComponent &instances = *geometry_set.get_component_for_read<InstancesComponent>();
GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
- const int domain_size = instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE);
+ const int domain_num = instances.attribute_domain_num(ATTR_DOMAIN_INSTANCE);
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ fn::FieldEvaluator evaluator{field_context, domain_num};
evaluator.set_selection(std::move(selection_field));
evaluator.add(std::move(position_field));
evaluator.add(std::move(radius_field));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
index 0e2803cd035..2067086c298 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -56,8 +56,8 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
int offset = 0;
for (const GeometryComponent *component : src_components) {
- const int domain_size = component->attribute_domain_size(domain);
- if (domain_size == 0) {
+ const int domain_num = component->attribute_domain_num(domain);
+ if (domain_num == 0) {
continue;
}
GVArray read_attribute = component->attribute_get_for_read(
@@ -66,9 +66,9 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
GVArray_GSpan src_span{read_attribute};
const void *src_buffer = src_span.data();
void *dst_buffer = dst_span[offset];
- cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_size);
+ cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_num);
- offset += domain_size;
+ offset += domain_num;
}
}
@@ -101,7 +101,7 @@ static void join_components(Span<const InstancesComponent *> src_components, Geo
int tot_instances = 0;
for (const InstancesComponent *src_component : src_components) {
- tot_instances += src_component->instances_amount();
+ tot_instances += src_component->instances_num();
}
dst_component.reserve(tot_instances);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
index 1ec97858d4d..1def4089115 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
@@ -39,9 +39,9 @@ static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_p
const float merge_distance,
const Field<bool> &selection_field)
{
- const int src_size = src_points.attribute_domain_size(ATTR_DOMAIN_POINT);
+ const int src_num = src_points.attribute_domain_num(ATTR_DOMAIN_POINT);
GeometryComponentFieldContext context{src_points, ATTR_DOMAIN_POINT};
- FieldEvaluator evaluator{context, src_size};
+ FieldEvaluator evaluator{context, src_num};
evaluator.add(selection_field);
evaluator.evaluate();
@@ -57,10 +57,10 @@ static std::optional<Mesh *> mesh_merge_by_distance_connected(const MeshComponen
const float merge_distance,
const Field<bool> &selection_field)
{
- const int src_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
- Array<bool> selection(src_size);
+ const int src_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT);
+ Array<bool> selection(src_num);
GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
- FieldEvaluator evaluator{context, src_size};
+ FieldEvaluator evaluator{context, src_num};
evaluator.add_with_destination(selection_field, selection.as_mutable_span());
evaluator.evaluate();
@@ -72,9 +72,9 @@ static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mes
const float merge_distance,
const Field<bool> &selection_field)
{
- const int src_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ const int src_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT);
GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
- FieldEvaluator evaluator{context, src_size};
+ FieldEvaluator evaluator{context, src_num};
evaluator.add(selection_field);
evaluator.evaluate();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
index 636ecb8ab41..0029b547375 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
@@ -6,414 +6,9 @@
#include "BKE_material.h"
#include "BKE_mesh.h"
-#include "node_geometry_util.hh"
-
-namespace blender::nodes {
-
-struct CuboidConfig {
- float3 size;
- int verts_x;
- int verts_y;
- int verts_z;
- int edges_x;
- int edges_y;
- int edges_z;
- int vertex_count;
- int poly_count;
- int loop_count;
-
- CuboidConfig(float3 size, int verts_x, int verts_y, int verts_z)
- : size(size),
- verts_x(verts_x),
- verts_y(verts_y),
- verts_z(verts_z),
- edges_x(verts_x - 1),
- edges_y(verts_y - 1),
- edges_z(verts_z - 1)
- {
- BLI_assert(edges_x > 0 && edges_y > 0 && edges_z > 0);
- this->vertex_count = this->get_vertex_count();
- this->poly_count = this->get_poly_count();
- this->loop_count = this->poly_count * 4;
- }
-
- private:
- int get_vertex_count()
- {
- const int inner_position_count = (verts_x - 2) * (verts_y - 2) * (verts_z - 2);
- return verts_x * verts_y * verts_z - inner_position_count;
- }
-
- int get_poly_count()
- {
- return 2 * (edges_x * edges_y + edges_y * edges_z + edges_z * edges_x);
- }
-};
-
-static void calculate_vertices(const CuboidConfig &config, MutableSpan<MVert> verts)
-{
- const float z_bottom = -config.size.z / 2.0f;
- const float z_delta = config.size.z / config.edges_z;
-
- const float x_left = -config.size.x / 2.0f;
- const float x_delta = config.size.x / config.edges_x;
-
- const float y_front = -config.size.y / 2.0f;
- const float y_delta = config.size.y / config.edges_y;
-
- int vert_index = 0;
-
- for (const int z : IndexRange(config.verts_z)) {
- if (ELEM(z, 0, config.edges_z)) {
- /* Fill bottom and top. */
- const float z_pos = z_bottom + z_delta * z;
- for (const int y : IndexRange(config.verts_y)) {
- const float y_pos = y_front + y_delta * y;
- for (const int x : IndexRange(config.verts_x)) {
- const float x_pos = x_left + x_delta * x;
- copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos));
- }
- }
- }
- else {
- for (const int y : IndexRange(config.verts_y)) {
- if (ELEM(y, 0, config.edges_y)) {
- /* Fill y-sides. */
- const float y_pos = y_front + y_delta * y;
- const float z_pos = z_bottom + z_delta * z;
- for (const int x : IndexRange(config.verts_x)) {
- const float x_pos = x_left + x_delta * x;
- copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos));
- }
- }
- else {
- /* Fill x-sides. */
- const float x_pos = x_left;
- const float y_pos = y_front + y_delta * y;
- const float z_pos = z_bottom + z_delta * z;
- copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos));
- const float x_pos2 = x_left + x_delta * config.edges_x;
- copy_v3_v3(verts[vert_index++].co, float3(x_pos2, y_pos, z_pos));
- }
- }
- }
- }
-}
-
-/* vert_1 = bottom left, vert_2 = bottom right, vert_3 = top right, vert_4 = top left.
- * Hence they are passed as 1,4,3,2 when calculating polys clockwise, and 1,2,3,4 for
- * anti-clockwise.
- */
-static void define_quad(MutableSpan<MPoly> polys,
- MutableSpan<MLoop> loops,
- const int poly_index,
- const int loop_index,
- const int vert_1,
- const int vert_2,
- const int vert_3,
- const int vert_4)
-{
- MPoly &poly = polys[poly_index];
- poly.loopstart = loop_index;
- poly.totloop = 4;
-
- MLoop &loop_1 = loops[loop_index];
- loop_1.v = vert_1;
- MLoop &loop_2 = loops[loop_index + 1];
- loop_2.v = vert_2;
- MLoop &loop_3 = loops[loop_index + 2];
- loop_3.v = vert_3;
- MLoop &loop_4 = loops[loop_index + 3];
- loop_4.v = vert_4;
-}
-
-static void calculate_polys(const CuboidConfig &config,
- MutableSpan<MPoly> polys,
- MutableSpan<MLoop> loops)
-{
- int loop_index = 0;
- int poly_index = 0;
-
- /* Number of vertices in an XY cross-section of the cube (barring top and bottom faces). */
- const int xy_cross_section_vert_count = config.verts_x * config.verts_y -
- (config.verts_x - 2) * (config.verts_y - 2);
+#include "GEO_mesh_primitive_cuboid.hh"
- /* Calculate polys for Bottom faces. */
- int vert_1_start = 0;
-
- for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) {
- for (const int x : IndexRange(config.edges_x)) {
- const int vert_1 = vert_1_start + x;
- const int vert_2 = vert_1_start + config.verts_x + x;
- const int vert_3 = vert_2 + 1;
- const int vert_4 = vert_1 + 1;
-
- define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4);
- loop_index += 4;
- poly_index++;
- }
- vert_1_start += config.verts_x;
- }
-
- /* Calculate polys for Front faces. */
- vert_1_start = 0;
- int vert_2_start = config.verts_x * config.verts_y;
-
- for ([[maybe_unused]] const int z : IndexRange(config.edges_z)) {
- for (const int x : IndexRange(config.edges_x)) {
- define_quad(polys,
- loops,
- poly_index,
- loop_index,
- vert_1_start + x,
- vert_1_start + x + 1,
- vert_2_start + x + 1,
- vert_2_start + x);
- loop_index += 4;
- poly_index++;
- }
- vert_1_start = vert_2_start;
- vert_2_start += config.verts_x * config.verts_y - (config.verts_x - 2) * (config.verts_y - 2);
- }
-
- /* Calculate polys for Top faces. */
- vert_1_start = config.verts_x * config.verts_y +
- (config.verts_z - 2) * (config.verts_x * config.verts_y -
- (config.verts_x - 2) * (config.verts_y - 2));
- vert_2_start = vert_1_start + config.verts_x;
-
- for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) {
- for (const int x : IndexRange(config.edges_x)) {
- define_quad(polys,
- loops,
- poly_index,
- loop_index,
- vert_1_start + x,
- vert_1_start + x + 1,
- vert_2_start + x + 1,
- vert_2_start + x);
- loop_index += 4;
- poly_index++;
- }
- vert_2_start += config.verts_x;
- vert_1_start += config.verts_x;
- }
-
- /* Calculate polys for Back faces. */
- vert_1_start = config.verts_x * config.edges_y;
- vert_2_start = vert_1_start + xy_cross_section_vert_count;
-
- for (const int z : IndexRange(config.edges_z)) {
- if (z == (config.edges_z - 1)) {
- vert_2_start += (config.verts_x - 2) * (config.verts_y - 2);
- }
- for (const int x : IndexRange(config.edges_x)) {
- define_quad(polys,
- loops,
- poly_index,
- loop_index,
- vert_1_start + x,
- vert_2_start + x,
- vert_2_start + x + 1,
- vert_1_start + x + 1);
- loop_index += 4;
- poly_index++;
- }
- vert_2_start += xy_cross_section_vert_count;
- vert_1_start += xy_cross_section_vert_count;
- }
-
- /* Calculate polys for Left faces. */
- vert_1_start = 0;
- vert_2_start = config.verts_x * config.verts_y;
-
- for (const int z : IndexRange(config.edges_z)) {
- for (const int y : IndexRange(config.edges_y)) {
- int vert_1;
- int vert_2;
- int vert_3;
- int vert_4;
-
- if (z == 0 || y == 0) {
- vert_1 = vert_1_start + config.verts_x * y;
- vert_4 = vert_1 + config.verts_x;
- }
- else {
- vert_1 = vert_1_start + 2 * y;
- vert_1 += config.verts_x - 2;
- vert_4 = vert_1 + 2;
- }
-
- if (y == 0 || z == (config.edges_z - 1)) {
- vert_2 = vert_2_start + config.verts_x * y;
- vert_3 = vert_2 + config.verts_x;
- }
- else {
- vert_2 = vert_2_start + 2 * y;
- vert_2 += config.verts_x - 2;
- vert_3 = vert_2 + 2;
- }
-
- define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4);
- loop_index += 4;
- poly_index++;
- }
- if (z == 0) {
- vert_1_start += config.verts_x * config.verts_y;
- }
- else {
- vert_1_start += xy_cross_section_vert_count;
- }
- vert_2_start += xy_cross_section_vert_count;
- }
-
- /* Calculate polys for Right faces. */
- vert_1_start = config.edges_x;
- vert_2_start = vert_1_start + config.verts_x * config.verts_y;
-
- for (const int z : IndexRange(config.edges_z)) {
- for (const int y : IndexRange(config.edges_y)) {
- int vert_1 = vert_1_start;
- int vert_2 = vert_2_start;
- int vert_3 = vert_2_start + 2;
- int vert_4 = vert_1 + config.verts_x;
-
- if (z == 0) {
- vert_1 = vert_1_start + config.verts_x * y;
- vert_4 = vert_1 + config.verts_x;
- }
- else {
- vert_1 = vert_1_start + 2 * y;
- vert_4 = vert_1 + 2;
- }
-
- if (z == (config.edges_z - 1)) {
- vert_2 = vert_2_start + config.verts_x * y;
- vert_3 = vert_2 + config.verts_x;
- }
- else {
- vert_2 = vert_2_start + 2 * y;
- vert_3 = vert_2 + 2;
- }
-
- if (y == (config.edges_y - 1)) {
- vert_3 = vert_2 + config.verts_x;
- vert_4 = vert_1 + config.verts_x;
- }
-
- define_quad(polys, loops, poly_index, loop_index, vert_1, vert_4, vert_3, vert_2);
- loop_index += 4;
- poly_index++;
- }
- if (z == 0) {
- vert_1_start += config.verts_x * config.verts_y;
- }
- else {
- vert_1_start += xy_cross_section_vert_count;
- }
- vert_2_start += xy_cross_section_vert_count;
- }
-}
-
-static void calculate_uvs(const CuboidConfig &config, Mesh *mesh)
-{
- MeshComponent mesh_component;
- mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- OutputAttribute_Typed<float2> uv_attribute =
- mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
- MutableSpan<float2> uvs = uv_attribute.as_span();
-
- int loop_index = 0;
-
- const float x_delta = 0.25f / static_cast<float>(config.edges_x);
- const float y_delta = 0.25f / static_cast<float>(config.edges_y);
- const float z_delta = 0.25f / static_cast<float>(config.edges_z);
-
- /* Calculate bottom face UVs. */
- for (const int y : IndexRange(config.edges_y)) {
- for (const int x : IndexRange(config.edges_x)) {
- uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - y * y_delta);
- uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - (y + 1) * y_delta);
- uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - (y + 1) * y_delta);
- uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - y * y_delta);
- }
- }
-
- /* Calculate front face UVs. */
- for (const int z : IndexRange(config.edges_z)) {
- for (const int x : IndexRange(config.edges_x)) {
- uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + z * z_delta);
- uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + z * z_delta);
- uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + (z + 1) * z_delta);
- uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + (z + 1) * z_delta);
- }
- }
-
- /* Calculate top face UVs. */
- for (const int y : IndexRange(config.edges_y)) {
- for (const int x : IndexRange(config.edges_x)) {
- uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + y * y_delta);
- uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + y * y_delta);
- uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + (y + 1) * y_delta);
- uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + (y + 1) * y_delta);
- }
- }
-
- /* Calculate back face UVs. */
- for (const int z : IndexRange(config.edges_z)) {
- for (const int x : IndexRange(config.edges_x)) {
- uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + z * z_delta);
- uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + (z + 1) * z_delta);
- uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + (z + 1) * z_delta);
- uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + z * z_delta);
- }
- }
-
- /* Calculate left face UVs. */
- for (const int z : IndexRange(config.edges_z)) {
- for (const int y : IndexRange(config.edges_y)) {
- uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + z * z_delta);
- uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + (z + 1) * z_delta);
- uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + (z + 1) * z_delta);
- uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + z * z_delta);
- }
- }
-
- /* Calculate right face UVs. */
- for (const int z : IndexRange(config.edges_z)) {
- for (const int y : IndexRange(config.edges_y)) {
- uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + z * z_delta);
- uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + z * z_delta);
- uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + (z + 1) * z_delta);
- uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + (z + 1) * z_delta);
- }
- }
-
- uv_attribute.save();
-}
-
-Mesh *create_cuboid_mesh(const float3 size,
- const int verts_x,
- const int verts_y,
- const int verts_z)
-{
- const CuboidConfig config(size, verts_x, verts_y, verts_z);
-
- Mesh *mesh = BKE_mesh_new_nomain(
- config.vertex_count, 0, 0, config.loop_count, config.poly_count);
- BKE_id_material_eval_ensure_default_slot(&mesh->id);
-
- calculate_vertices(config, {mesh->mvert, mesh->totvert});
-
- calculate_polys(config, {mesh->mpoly, mesh->totpoly}, {mesh->mloop, mesh->totloop});
- BKE_mesh_calc_edges(mesh, false, false);
-
- calculate_uvs(config, mesh);
-
- return mesh;
-}
-
-} // namespace blender::nodes
+#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_mesh_primitive_cube_cc {
@@ -442,6 +37,16 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Mesh"));
}
+static Mesh *create_cuboid_mesh(const float3 &size,
+ const int verts_x,
+ const int verts_y,
+ const int verts_z)
+{
+ Mesh *mesh = geometry::create_cuboid_mesh(size, verts_x, verts_y, verts_z, "uv_map");
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+ return mesh;
+}
+
static Mesh *create_cube_mesh(const float3 size,
const int verts_x,
const int verts_y,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
index f6ee3d00dee..ec6acf55dd8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
@@ -25,7 +25,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
GeometryComponentFieldContext context{component, ATTR_DOMAIN_EDGE};
- fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_EDGE)};
+ fn::FieldEvaluator evaluator{context, component.attribute_domain_num(ATTR_DOMAIN_EDGE)};
evaluator.add(params.get_input<Field<bool>>("Selection"));
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
index 1a0cc53cb6c..6b23b685549 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BLI_task.hh"
+
#include "DNA_pointcloud_types.h"
#include "BKE_attribute_math.hh"
@@ -41,14 +43,15 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-template<typename T>
-static void copy_attribute_to_points(const VArray<T> &src,
- const IndexMask mask,
- MutableSpan<T> dst)
+static void materialize_compressed_to_uninitialized_threaded(const GVArray &src,
+ const IndexMask mask,
+ GMutableSpan dst)
{
- for (const int i : mask.index_range()) {
- dst[i] = src[mask[i]];
- }
+ BLI_assert(src.type() == dst.type());
+ BLI_assert(mask.size() == dst.size());
+ threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) {
+ src.materialize_compressed_to_uninitialized(mask.slice(range), dst.slice(range).data());
+ });
}
static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
@@ -63,12 +66,12 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
return;
}
GeometryComponentFieldContext field_context{*mesh_component, domain};
- const int domain_size = mesh_component->attribute_domain_size(domain);
- if (domain_size == 0) {
+ const int domain_num = mesh_component->attribute_domain_num(domain);
+ if (domain_num == 0) {
geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
return;
}
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ fn::FieldEvaluator evaluator{field_context, domain_num};
evaluator.set_selection(selection_field);
/* Evaluating directly into the point cloud doesn't work because we are not using the full
* "min_array_size" array but compressing the selected elements into the final array with no
@@ -79,16 +82,21 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
- uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f);
geometry_set.replace_pointcloud(pointcloud);
PointCloudComponent &point_component =
geometry_set.get_component_for_write<PointCloudComponent>();
- copy_attribute_to_points(evaluator.get_evaluated<float3>(0),
- selection,
- {(float3 *)pointcloud->co, pointcloud->totpoint});
- copy_attribute_to_points(
- evaluator.get_evaluated<float>(1), selection, {pointcloud->radius, pointcloud->totpoint});
+ OutputAttribute position = point_component.attribute_try_get_for_output_only(
+ "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ materialize_compressed_to_uninitialized_threaded(
+ evaluator.get_evaluated(0), selection, position.as_span());
+ position.save();
+
+ OutputAttribute radius = point_component.attribute_try_get_for_output_only(
+ "radius", ATTR_DOMAIN_POINT, CD_PROP_FLOAT);
+ materialize_compressed_to_uninitialized_threaded(
+ evaluator.get_evaluated(1), selection, radius.as_span());
+ radius.save();
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
@@ -102,11 +110,7 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
OutputAttribute dst = point_component.attribute_try_get_for_output_only(
attribute_id, ATTR_DOMAIN_POINT, data_type);
if (dst && src) {
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
- using T = decltype(dummy);
- VArray<T> src_typed = src.typed<T>();
- copy_attribute_to_points(src_typed, selection, dst.as_span().typed<T>());
- });
+ materialize_compressed_to_uninitialized_threaded(src, selection, dst.as_span());
dst.save();
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
index 0f6586617bc..577b001fd06 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
@@ -38,13 +38,13 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
}
GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT};
- const int domain_size = point_component->attribute_domain_size(ATTR_DOMAIN_POINT);
- if (domain_size == 0) {
+ const int domain_num = point_component->attribute_domain_num(ATTR_DOMAIN_POINT);
+ if (domain_num == 0) {
geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
return;
}
- fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ fn::FieldEvaluator selection_evaluator{field_context, domain_num};
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
index c99b51ffd4c..42cee4c0efe 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
@@ -168,14 +168,14 @@ static void gather_point_data_from_component(GeoNodeExecParams &params,
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 &params, 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 &params, 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 &params, InstancesComponent &i
{
GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE};
- fn::FieldEvaluator evaluator{field_context, instances_component.instances_amount()};
+ fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()};
evaluator.set_selection(params.extract_input<Field<bool>>("Selection"));
evaluator.add(params.extract_input<Field<float3>>("Translation"));
evaluator.add(params.extract_input<Field<bool>>("Local Space"));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
index 992470e8279..e47dc22da04 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
@@ -83,9 +83,9 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometryComponent &component = geometry_set.get_component_for_write<MeshComponent>();
const Mesh &mesh_in = *geometry_set.get_mesh_for_read();
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
+ const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE);
GeometryComponentFieldContext context{component, ATTR_DOMAIN_FACE};
- FieldEvaluator evaluator{context, domain_size};
+ FieldEvaluator evaluator{context, domain_num};
evaluator.add(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
index 378bac894e8..9a316190720 100644
--- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc
+++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
@@ -241,27 +241,27 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful
case GEO_COMPONENT_TYPE_MESH: {
const MeshComponent &mesh_component = *(const MeshComponent *)component;
MeshInfo &info = this->mesh_info.emplace();
- info.tot_verts = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
- info.tot_edges = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
- info.tot_faces = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE);
+ info.verts_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT);
+ info.edges_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE);
+ info.faces_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_FACE);
break;
}
case GEO_COMPONENT_TYPE_CURVE: {
const CurveComponent &curve_component = *(const CurveComponent *)component;
CurveInfo &info = this->curve_info.emplace();
- info.tot_splines = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE);
+ info.splines_num = curve_component.attribute_domain_num(ATTR_DOMAIN_CURVE);
break;
}
case GEO_COMPONENT_TYPE_POINT_CLOUD: {
const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component;
PointCloudInfo &info = this->pointcloud_info.emplace();
- info.tot_points = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ info.points_num = pointcloud_component.attribute_domain_num(ATTR_DOMAIN_POINT);
break;
}
case GEO_COMPONENT_TYPE_INSTANCES: {
const InstancesComponent &instances_component = *(const InstancesComponent *)component;
InstancesInfo &info = this->instances_info.emplace();
- info.tot_instances = instances_component.instances_amount();
+ info.instances_num = instances_component.instances_num();
break;
}
case GEO_COMPONENT_TYPE_VOLUME: {
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index cea3084a418..39d8c453e43 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -119,14 +119,14 @@ GVArray GeoNodeExecParams::get_input_attribute(const StringRef name,
const bNodeSocket *found_socket = this->find_available_socket(name);
BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(type);
- const int64_t domain_size = component.attribute_domain_size(domain);
+ const int64_t domain_num = component.attribute_domain_num(domain);
if (default_value == nullptr) {
default_value = cpp_type->default_value();
}
if (found_socket == nullptr) {
- return GVArray::ForSingle(*cpp_type, domain_size, default_value);
+ return GVArray::ForSingle(*cpp_type, domain_num, default_value);
}
if (found_socket->type == SOCK_STRING) {
@@ -140,40 +140,40 @@ GVArray GeoNodeExecParams::get_input_attribute(const StringRef name,
/* If the attribute doesn't exist, use the default value and output an error message
* (except when the field is empty, to avoid spamming error messages, and not when
* the domain is empty and we don't expect an attribute anyway). */
- if (!name.empty() && component.attribute_domain_size(domain) != 0) {
+ if (!name.empty() && component.attribute_domain_num(domain) != 0) {
this->error_message_add(NodeWarningType::Error,
TIP_("No attribute with name \"") + name + "\"");
}
- return GVArray::ForSingle(*cpp_type, domain_size, default_value);
+ return GVArray::ForSingle(*cpp_type, domain_num, default_value);
}
const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions();
if (found_socket->type == SOCK_FLOAT) {
const float value = this->get_input<float>(found_socket->identifier);
BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
conversions.convert_to_uninitialized(CPPType::get<float>(), *cpp_type, &value, buffer);
- return GVArray::ForSingle(*cpp_type, domain_size, buffer);
+ return GVArray::ForSingle(*cpp_type, domain_num, buffer);
}
if (found_socket->type == SOCK_INT) {
const int value = this->get_input<int>(found_socket->identifier);
BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
conversions.convert_to_uninitialized(CPPType::get<int>(), *cpp_type, &value, buffer);
- return GVArray::ForSingle(*cpp_type, domain_size, buffer);
+ return GVArray::ForSingle(*cpp_type, domain_num, buffer);
}
if (found_socket->type == SOCK_VECTOR) {
const float3 value = this->get_input<float3>(found_socket->identifier);
BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
conversions.convert_to_uninitialized(CPPType::get<float3>(), *cpp_type, &value, buffer);
- return GVArray::ForSingle(*cpp_type, domain_size, buffer);
+ return GVArray::ForSingle(*cpp_type, domain_num, buffer);
}
if (found_socket->type == SOCK_RGBA) {
const ColorGeometry4f value = this->get_input<ColorGeometry4f>(found_socket->identifier);
BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
conversions.convert_to_uninitialized(
CPPType::get<ColorGeometry4f>(), *cpp_type, &value, buffer);
- return GVArray::ForSingle(*cpp_type, domain_size, buffer);
+ return GVArray::ForSingle(*cpp_type, domain_num, buffer);
}
BLI_assert(false);
- return GVArray::ForSingle(*cpp_type, domain_size, default_value);
+ return GVArray::ForSingle(*cpp_type, domain_num, default_value);
}
CustomDataType GeoNodeExecParams::get_input_attribute_data_type(
diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c
index 5d2e1663ae3..e8be093c606 100644
--- a/source/blender/nodes/intern/node_util.c
+++ b/source/blender/nodes/intern/node_util.c
@@ -226,6 +226,39 @@ void node_filter_label(const bNodeTree *UNUSED(ntree), const bNode *node, char *
BLI_strncpy(label, IFACE_(name), maxlen);
}
+void node_combsep_color_label(const ListBase *sockets, NodeCombSepColorMode mode)
+{
+ bNodeSocket *sock1 = (bNodeSocket *)sockets->first;
+ bNodeSocket *sock2 = sock1->next;
+ bNodeSocket *sock3 = sock2->next;
+
+ node_sock_label_clear(sock1);
+ node_sock_label_clear(sock2);
+ node_sock_label_clear(sock3);
+
+ switch (mode) {
+ case NODE_COMBSEP_COLOR_RGB:
+ node_sock_label(sock1, "Red");
+ node_sock_label(sock2, "Green");
+ node_sock_label(sock3, "Blue");
+ break;
+ case NODE_COMBSEP_COLOR_HSL:
+ node_sock_label(sock1, "Hue");
+ node_sock_label(sock2, "Saturation");
+ node_sock_label(sock3, "Lightness");
+ break;
+ case NODE_COMBSEP_COLOR_HSV:
+ node_sock_label(sock1, "Hue");
+ node_sock_label(sock2, "Saturation");
+ node_sock_label(sock3, "Value");
+ break;
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/nodes/intern/node_util.h b/source/blender/nodes/intern/node_util.h
index de03a176c0c..0a2a7a70091 100644
--- a/source/blender/nodes/intern/node_util.h
+++ b/source/blender/nodes/intern/node_util.h
@@ -65,6 +65,7 @@ void node_filter_label(const struct bNodeTree *ntree,
const struct bNode *node,
char *label,
int maxlen);
+void node_combsep_color_label(const ListBase *sockets, NodeCombSepColorMode mode);
/*** Link Handling */
diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt
index 9b4ea0e0db6..3e90f185168 100644
--- a/source/blender/nodes/shader/CMakeLists.txt
+++ b/source/blender/nodes/shader/CMakeLists.txt
@@ -82,6 +82,7 @@ set(SRC
nodes/node_shader_rgb.cc
nodes/node_shader_rgb_to_bw.cc
nodes/node_shader_script.cc
+ nodes/node_shader_sepcomb_color.cc
nodes/node_shader_sepcomb_hsv.cc
nodes/node_shader_sepcomb_rgb.cc
nodes/node_shader_sepcomb_xyz.cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc
index b1db0248d9f..eb47059063d 100644
--- a/source/blender/nodes/shader/nodes/node_shader_curves.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc
@@ -28,48 +28,34 @@ static int gpu_shader_curve_vec(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- float *array, layer;
- int size;
-
- CurveMapping *cumap = (CurveMapping *)node->storage;
-
- BKE_curvemapping_init(cumap);
- BKE_curvemapping_table_RGBA(cumap, &array, &size);
- GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer);
-
- float ext_xyz[3][4];
- float range_xyz[3];
-
- for (int a = 0; a < 3; a++) {
- const CurveMap *cm = &cumap->cm[a];
- ext_xyz[a][0] = cm->mintable;
- ext_xyz[a][2] = cm->maxtable;
- range_xyz[a] = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable);
- /* Compute extrapolation gradients. */
- if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) {
- ext_xyz[a][1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_xyz[a])) :
- 1e8f;
- ext_xyz[a][3] = (cm->ext_out[0] != 0.0f) ?
- (cm->ext_out[1] / (cm->ext_out[0] * range_xyz[a])) :
- 1e8f;
- }
- else {
- ext_xyz[a][1] = 0.0f;
- ext_xyz[a][3] = 0.0f;
- }
- }
+ CurveMapping *curve_mapping = (CurveMapping *)node->storage;
+
+ BKE_curvemapping_init(curve_mapping);
+ float *band_values;
+ int band_size;
+ BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
+ float band_layer;
+ GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer);
+
+ float start_slopes[CM_TOT];
+ float end_slopes[CM_TOT];
+ BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
+ float range_minimums[CM_TOT];
+ BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
+ float range_dividers[CM_TOT];
+ BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
return GPU_stack_link(mat,
node,
- "curves_vec",
+ "curves_vector_mixed",
in,
out,
- tex,
- GPU_constant(&layer),
- GPU_uniform(range_xyz),
- GPU_uniform(ext_xyz[0]),
- GPU_uniform(ext_xyz[1]),
- GPU_uniform(ext_xyz[2]));
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(range_minimums),
+ GPU_uniform(range_dividers),
+ GPU_uniform(start_slopes),
+ GPU_uniform(end_slopes));
}
class CurveVecFunction : public fn::MultiFunction {
@@ -157,72 +143,59 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- float *array, layer;
- int size;
- bool use_opti = true;
-
- CurveMapping *cumap = (CurveMapping *)node->storage;
-
- BKE_curvemapping_init(cumap);
- BKE_curvemapping_table_RGBA(cumap, &array, &size);
- GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer);
-
- float ext_rgba[4][4];
- float range_rgba[4];
-
- for (int a = 0; a < CM_TOT; a++) {
- const CurveMap *cm = &cumap->cm[a];
- ext_rgba[a][0] = cm->mintable;
- ext_rgba[a][2] = cm->maxtable;
- range_rgba[a] = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable);
- /* Compute extrapolation gradients. */
- if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) {
- ext_rgba[a][1] = (cm->ext_in[0] != 0.0f) ?
- (cm->ext_in[1] / (cm->ext_in[0] * range_rgba[a])) :
- 1e8f;
- ext_rgba[a][3] = (cm->ext_out[0] != 0.0f) ?
- (cm->ext_out[1] / (cm->ext_out[0] * range_rgba[a])) :
- 1e8f;
- }
- else {
- ext_rgba[a][1] = 0.0f;
- ext_rgba[a][3] = 0.0f;
- }
-
- /* Check if rgb comps are just linear. */
- if (a < 3) {
- if (range_rgba[a] != 1.0f || ext_rgba[a][1] != 1.0f || ext_rgba[a][2] != 1.0f ||
- cm->totpoint != 2 || cm->curve[0].x != 0.0f || cm->curve[0].y != 0.0f ||
- cm->curve[1].x != 1.0f || cm->curve[1].y != 1.0f) {
- use_opti = false;
- }
- }
- }
-
- if (use_opti) {
+ CurveMapping *curve_mapping = (CurveMapping *)node->storage;
+
+ BKE_curvemapping_init(curve_mapping);
+ float *band_values;
+ int band_size;
+ BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
+ float band_layer;
+ GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer);
+
+ float start_slopes[CM_TOT];
+ float end_slopes[CM_TOT];
+ BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
+ float range_minimums[CM_TOT];
+ BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
+ float range_dividers[CM_TOT];
+ BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
+
+ /* Shader nodes don't do white balancing. */
+ float black_level[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ float white_level[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+
+ /* If the RGB curves do nothing, use a function that skips RGB computations. */
+ if (BKE_curvemapping_is_map_identity(curve_mapping, 0) &&
+ BKE_curvemapping_is_map_identity(curve_mapping, 1) &&
+ BKE_curvemapping_is_map_identity(curve_mapping, 2)) {
return GPU_stack_link(mat,
node,
- "curves_rgb_opti",
+ "curves_combined_only",
in,
out,
- tex,
- GPU_constant(&layer),
- GPU_uniform(range_rgba),
- GPU_uniform(ext_rgba[3]));
+ GPU_constant(black_level),
+ GPU_constant(white_level),
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(&range_minimums[3]),
+ GPU_uniform(&range_dividers[3]),
+ GPU_uniform(&start_slopes[3]),
+ GPU_uniform(&end_slopes[3]));
}
return GPU_stack_link(mat,
node,
- "curves_rgb",
+ "curves_combined_rgb",
in,
out,
- tex,
- GPU_constant(&layer),
- GPU_uniform(range_rgba),
- GPU_uniform(ext_rgba[0]),
- GPU_uniform(ext_rgba[1]),
- GPU_uniform(ext_rgba[2]),
- GPU_uniform(ext_rgba[3]));
+ GPU_constant(black_level),
+ GPU_constant(white_level),
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(range_minimums),
+ GPU_uniform(range_dividers),
+ GPU_uniform(start_slopes),
+ GPU_uniform(end_slopes));
}
class CurveRGBFunction : public fn::MultiFunction {
@@ -316,40 +289,34 @@ static int gpu_shader_curve_float(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- float *array, layer;
- int size;
-
- CurveMapping *cumap = (CurveMapping *)node->storage;
+ CurveMapping *curve_mapping = (CurveMapping *)node->storage;
+
+ BKE_curvemapping_init(curve_mapping);
+ float *band_values;
+ int band_size;
+ BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
+ float band_layer;
+ GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer);
+
+ float start_slopes[CM_TOT];
+ float end_slopes[CM_TOT];
+ BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
+ float range_minimums[CM_TOT];
+ BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
+ float range_dividers[CM_TOT];
+ BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
- BKE_curvemapping_init(cumap);
- BKE_curvemapping_table_F(cumap, &array, &size);
- GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer);
-
- float ext_xyz[4];
- float range_x;
-
- const CurveMap *cm = &cumap->cm[0];
- ext_xyz[0] = cm->mintable;
- ext_xyz[2] = cm->maxtable;
- range_x = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable);
- /* Compute extrapolation gradients. */
- if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) {
- ext_xyz[1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_x)) : 1e8f;
- ext_xyz[3] = (cm->ext_out[0] != 0.0f) ? (cm->ext_out[1] / (cm->ext_out[0] * range_x)) : 1e8f;
- }
- else {
- ext_xyz[1] = 0.0f;
- ext_xyz[3] = 0.0f;
- }
return GPU_stack_link(mat,
node,
- "curve_float",
+ "curves_float_mixed",
in,
out,
- tex,
- GPU_constant(&layer),
- GPU_uniform(&range_x),
- GPU_uniform(ext_xyz));
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(range_minimums),
+ GPU_uniform(range_dividers),
+ GPU_uniform(start_slopes),
+ GPU_uniform(end_slopes));
}
class CurveFloatFunction : public fn::MultiFunction {
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_color.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_color.cc
new file mode 100644
index 00000000000..8e378ebabce
--- /dev/null
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_color.cc
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup shdnodes
+ */
+
+#include "node_shader_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+static void node_combsep_color_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeCombSepColor *data = MEM_cnew<NodeCombSepColor>(__func__);
+ data->mode = NODE_COMBSEP_COLOR_RGB;
+ node->storage = data;
+}
+
+/* **************** SEPARATE COLOR ******************** */
+
+namespace blender::nodes::node_shader_separate_color_cc {
+
+NODE_STORAGE_FUNCS(NodeCombSepColor)
+
+static void sh_node_sepcolor_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
+ b.add_output<decl::Float>(N_("Red"));
+ b.add_output<decl::Float>(N_("Green"));
+ b.add_output<decl::Float>(N_("Blue"));
+}
+
+static void node_sepcolor_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeCombSepColor &storage = node_storage(*node);
+ node_combsep_color_label(&node->outputs, (NodeCombSepColorMode)storage.mode);
+}
+
+static const char *gpu_shader_get_name(int mode)
+{
+ switch (mode) {
+ case NODE_COMBSEP_COLOR_RGB:
+ return "separate_color_rgb";
+ case NODE_COMBSEP_COLOR_HSV:
+ return "separate_color_hsv";
+ case NODE_COMBSEP_COLOR_HSL:
+ return "separate_color_hsl";
+ }
+
+ return nullptr;
+}
+
+static int gpu_shader_sepcolor(GPUMaterial *mat,
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *out)
+{
+ const NodeCombSepColor &storage = node_storage(*node);
+ const char *name = gpu_shader_get_name(storage.mode);
+ if (name != nullptr) {
+ return GPU_stack_link(mat, node, name, in, out);
+ }
+
+ return 0;
+}
+
+} // namespace blender::nodes::node_shader_separate_color_cc
+
+void register_node_type_sh_sepcolor()
+{
+ namespace file_ns = blender::nodes::node_shader_separate_color_cc;
+
+ static bNodeType ntype;
+
+ sh_node_type_base(&ntype, SH_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_CONVERTER);
+ ntype.declare = file_ns::sh_node_sepcolor_declare;
+ node_type_update(&ntype, file_ns::node_sepcolor_update);
+ node_type_init(&ntype, node_combsep_color_init);
+ node_type_storage(
+ &ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage);
+ node_type_gpu(&ntype, file_ns::gpu_shader_sepcolor);
+
+ nodeRegisterType(&ntype);
+}
+
+/* **************** COMBINE COLOR ******************** */
+
+namespace blender::nodes::node_shader_combine_color_cc {
+
+NODE_STORAGE_FUNCS(NodeCombSepColor)
+
+static void sh_node_combcolor_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Float>(N_("Green"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR);
+ b.add_input<decl::Float>(N_("Blue"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR);
+ b.add_output<decl::Color>(N_("Color"));
+}
+
+static void node_combcolor_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeCombSepColor &storage = node_storage(*node);
+ node_combsep_color_label(&node->inputs, (NodeCombSepColorMode)storage.mode);
+}
+
+static const char *gpu_shader_get_name(int mode)
+{
+ switch (mode) {
+ case NODE_COMBSEP_COLOR_RGB:
+ return "combine_color_rgb";
+ case NODE_COMBSEP_COLOR_HSV:
+ return "combine_color_hsv";
+ case NODE_COMBSEP_COLOR_HSL:
+ return "combine_color_hsl";
+ }
+
+ return nullptr;
+}
+
+static int gpu_shader_combcolor(GPUMaterial *mat,
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *out)
+{
+ const NodeCombSepColor &storage = node_storage(*node);
+ const char *name = gpu_shader_get_name(storage.mode);
+ if (name != nullptr) {
+ return GPU_stack_link(mat, node, name, in, out);
+ }
+
+ return 0;
+}
+
+} // namespace blender::nodes::node_shader_combine_color_cc
+
+void register_node_type_sh_combcolor()
+{
+ namespace file_ns = blender::nodes::node_shader_combine_color_cc;
+
+ static bNodeType ntype;
+
+ sh_node_type_base(&ntype, SH_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_CONVERTER);
+ ntype.declare = file_ns::sh_node_combcolor_declare;
+ node_type_update(&ntype, file_ns::node_combcolor_update);
+ node_type_init(&ntype, node_combsep_color_init);
+ node_type_storage(
+ &ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage);
+ node_type_gpu(&ntype, file_ns::gpu_shader_combcolor);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc
index 129c8cf4b97..6dfabe48292 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc
@@ -36,7 +36,7 @@ void register_node_type_sh_sephsv()
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_SEPHSV, "Separate HSV", NODE_CLASS_CONVERTER);
+ sh_node_type_base(&ntype, SH_NODE_SEPHSV_LEGACY, "Separate HSV", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::node_declare_sephsv;
node_type_gpu(&ntype, file_ns::gpu_shader_sephsv);
@@ -72,7 +72,7 @@ void register_node_type_sh_combhsv()
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_COMBHSV, "Combine HSV", NODE_CLASS_CONVERTER);
+ sh_node_type_base(&ntype, SH_NODE_COMBHSV_LEGACY, "Combine HSV", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::node_declare_combhsv;
node_type_gpu(&ntype, file_ns::gpu_shader_combhsv);
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc
index 657f591a50c..28b55047633 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc
@@ -76,7 +76,7 @@ void register_node_type_sh_seprgb()
static bNodeType ntype;
- sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB, "Separate RGB", NODE_CLASS_CONVERTER);
+ sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB_LEGACY, "Separate RGB", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::sh_node_seprgb_declare;
node_type_gpu(&ntype, file_ns::gpu_shader_seprgb);
ntype.build_multi_function = file_ns::sh_node_seprgb_build_multi_function;
@@ -119,7 +119,7 @@ void register_node_type_sh_combrgb()
static bNodeType ntype;
- sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB, "Combine RGB", NODE_CLASS_CONVERTER);
+ sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB_LEGACY, "Combine RGB", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::sh_node_combrgb_declare;
node_type_gpu(&ntype, file_ns::gpu_shader_combrgb);
ntype.build_multi_function = file_ns::sh_node_combrgb_build_multi_function;
diff --git a/source/blender/nodes/texture/CMakeLists.txt b/source/blender/nodes/texture/CMakeLists.txt
index 7706f118507..5bed54ebfd7 100644
--- a/source/blender/nodes/texture/CMakeLists.txt
+++ b/source/blender/nodes/texture/CMakeLists.txt
@@ -24,6 +24,7 @@ set(SRC
nodes/node_texture_at.c
nodes/node_texture_bricks.c
nodes/node_texture_checker.c
+ nodes/node_texture_combine_color.c
nodes/node_texture_common.c
nodes/node_texture_compose.c
nodes/node_texture_coord.c
@@ -39,6 +40,7 @@ set(SRC
nodes/node_texture_proc.c
nodes/node_texture_rotate.c
nodes/node_texture_scale.c
+ nodes/node_texture_separate_color.c
nodes/node_texture_texture.c
nodes/node_texture_translate.c
nodes/node_texture_valToNor.c
diff --git a/source/blender/nodes/texture/node_texture_util.c b/source/blender/nodes/texture/node_texture_util.c
index 76208104a8c..248114f242a 100644
--- a/source/blender/nodes/texture/node_texture_util.c
+++ b/source/blender/nodes/texture/node_texture_util.c
@@ -49,7 +49,7 @@ static void tex_call_delegate(TexDelegate *dg, float *out, TexParams *params, sh
}
}
-static void tex_input(float *out, int sz, bNodeStack *in, TexParams *params, short thread)
+static void tex_input(float *out, int num, bNodeStack *in, TexParams *params, short thread)
{
TexDelegate *dg = in->data;
if (dg) {
@@ -59,7 +59,7 @@ static void tex_input(float *out, int sz, bNodeStack *in, TexParams *params, sho
in->vec[1] = in->vec[2] = in->vec[0];
}
}
- memcpy(out, in->vec, sz * sizeof(float));
+ memcpy(out, in->vec, num * sizeof(float));
}
void tex_input_vec(float *out, bNodeStack *in, TexParams *params, short thread)
diff --git a/source/blender/nodes/texture/nodes/node_texture_combine_color.c b/source/blender/nodes/texture/nodes/node_texture_combine_color.c
new file mode 100644
index 00000000000..459553bc950
--- /dev/null
+++ b/source/blender/nodes/texture/nodes/node_texture_combine_color.c
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup texnodes
+ */
+
+#include "BLI_listbase.h"
+#include "NOD_texture.h"
+#include "node_texture_util.h"
+
+static bNodeSocketTemplate inputs[] = {
+ {SOCK_FLOAT, N_("Red"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
+ {SOCK_FLOAT, N_("Green"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
+ {SOCK_FLOAT, N_("Blue"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
+ {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
+ {-1, ""},
+};
+static bNodeSocketTemplate outputs[] = {
+ {SOCK_RGBA, N_("Color")},
+ {-1, ""},
+};
+
+static void colorfn(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread)
+{
+ int i;
+ for (i = 0; i < 4; i++) {
+ out[i] = tex_input_value(in[i], p, thread);
+ }
+ /* Apply color space if required. */
+ switch (node->custom1) {
+ case NODE_COMBSEP_COLOR_RGB: {
+ /* Pass */
+ break;
+ }
+ case NODE_COMBSEP_COLOR_HSV: {
+ hsv_to_rgb_v(out, out);
+ break;
+ }
+ case NODE_COMBSEP_COLOR_HSL: {
+ hsl_to_rgb_v(out, out);
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+}
+
+static void update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ node_combsep_color_label(&node->inputs, (NodeCombSepColorMode)node->custom1);
+}
+
+static void exec(void *data,
+ int UNUSED(thread),
+ bNode *node,
+ bNodeExecData *execdata,
+ bNodeStack **in,
+ bNodeStack **out)
+{
+ tex_output(node, execdata, in, out[0], &colorfn, data);
+}
+
+void register_node_type_tex_combine_color(void)
+{
+ static bNodeType ntype;
+
+ tex_node_type_base(&ntype, TEX_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_OP_COLOR);
+ node_type_socket_templates(&ntype, inputs, outputs);
+ node_type_exec(&ntype, NULL, NULL, exec);
+ node_type_update(&ntype, update);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/texture/nodes/node_texture_compose.c b/source/blender/nodes/texture/nodes/node_texture_compose.c
index e341b65ac97..ef14062c72d 100644
--- a/source/blender/nodes/texture/nodes/node_texture_compose.c
+++ b/source/blender/nodes/texture/nodes/node_texture_compose.c
@@ -42,7 +42,7 @@ void register_node_type_tex_compose(void)
{
static bNodeType ntype;
- tex_node_type_base(&ntype, TEX_NODE_COMPOSE, "Combine RGBA", NODE_CLASS_OP_COLOR);
+ tex_node_type_base(&ntype, TEX_NODE_COMPOSE_LEGACY, "Combine RGBA", NODE_CLASS_OP_COLOR);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_exec(&ntype, NULL, NULL, exec);
diff --git a/source/blender/nodes/texture/nodes/node_texture_decompose.c b/source/blender/nodes/texture/nodes/node_texture_decompose.c
index 21c3944e255..2d42fa4602e 100644
--- a/source/blender/nodes/texture/nodes/node_texture_decompose.c
+++ b/source/blender/nodes/texture/nodes/node_texture_decompose.c
@@ -62,7 +62,7 @@ void register_node_type_tex_decompose(void)
{
static bNodeType ntype;
- tex_node_type_base(&ntype, TEX_NODE_DECOMPOSE, "Separate RGBA", NODE_CLASS_OP_COLOR);
+ tex_node_type_base(&ntype, TEX_NODE_DECOMPOSE_LEGACY, "Separate RGBA", NODE_CLASS_OP_COLOR);
node_type_socket_templates(&ntype, inputs, outputs);
node_type_exec(&ntype, NULL, NULL, exec);
diff --git a/source/blender/nodes/texture/nodes/node_texture_separate_color.c b/source/blender/nodes/texture/nodes/node_texture_separate_color.c
new file mode 100644
index 00000000000..a482a3f3421
--- /dev/null
+++ b/source/blender/nodes/texture/nodes/node_texture_separate_color.c
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup texnodes
+ */
+
+#include "BLI_listbase.h"
+#include "NOD_texture.h"
+#include "node_texture_util.h"
+#include <math.h>
+
+static bNodeSocketTemplate inputs[] = {
+ {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f},
+ {-1, ""},
+};
+static bNodeSocketTemplate outputs[] = {
+ {SOCK_FLOAT, N_("Red")},
+ {SOCK_FLOAT, N_("Green")},
+ {SOCK_FLOAT, N_("Blue")},
+ {SOCK_FLOAT, N_("Alpha")},
+ {-1, ""},
+};
+
+static void apply_color_space(float *out, NodeCombSepColorMode type)
+{
+ switch (type) {
+ case NODE_COMBSEP_COLOR_RGB: {
+ /* Pass */
+ break;
+ }
+ case NODE_COMBSEP_COLOR_HSV: {
+ rgb_to_hsv_v(out, out);
+ break;
+ }
+ case NODE_COMBSEP_COLOR_HSL: {
+ rgb_to_hsl_v(out, out);
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+}
+
+static void valuefn_r(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread)
+{
+ tex_input_rgba(out, in[0], p, thread);
+ apply_color_space(out, (NodeCombSepColorMode)node->custom1);
+ *out = out[0];
+}
+
+static void valuefn_g(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread)
+{
+ tex_input_rgba(out, in[0], p, thread);
+ apply_color_space(out, (NodeCombSepColorMode)node->custom1);
+ *out = out[1];
+}
+
+static void valuefn_b(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread)
+{
+ tex_input_rgba(out, in[0], p, thread);
+ apply_color_space(out, (NodeCombSepColorMode)node->custom1);
+ *out = out[2];
+}
+
+static void valuefn_a(float *out, TexParams *p, bNode *UNUSED(node), bNodeStack **in, short thread)
+{
+ tex_input_rgba(out, in[0], p, thread);
+ *out = out[3];
+}
+
+static void update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ node_combsep_color_label(&node->outputs, (NodeCombSepColorMode)node->custom1);
+}
+
+static void exec(void *data,
+ int UNUSED(thread),
+ bNode *node,
+ bNodeExecData *execdata,
+ bNodeStack **in,
+ bNodeStack **out)
+{
+ tex_output(node, execdata, in, out[0], &valuefn_r, data);
+ tex_output(node, execdata, in, out[1], &valuefn_g, data);
+ tex_output(node, execdata, in, out[2], &valuefn_b, data);
+ tex_output(node, execdata, in, out[3], &valuefn_a, data);
+}
+
+void register_node_type_tex_separate_color(void)
+{
+ static bNodeType ntype;
+
+ tex_node_type_base(&ntype, TEX_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_OP_COLOR);
+ node_type_socket_templates(&ntype, inputs, outputs);
+ node_type_exec(&ntype, NULL, NULL, exec);
+ node_type_update(&ntype, update);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c
index baa2b0deb71..972a782d650 100644
--- a/source/blender/python/bmesh/bmesh_py_types.c
+++ b/source/blender/python/bmesh/bmesh_py_types.c
@@ -1259,10 +1259,16 @@ static PyObject *bpy_bmesh_select_flush(BPy_BMesh *self, PyObject *value)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpy_bmesh_normal_update_doc,
- ".. method:: normal_update()\n"
- "\n"
- " Update mesh normals.\n");
+PyDoc_STRVAR(
+ bpy_bmesh_normal_update_doc,
+ ".. method:: normal_update()\n"
+ "\n"
+ " Update normals of mesh faces and verts.\n"
+ "\n"
+ " .. note::\n"
+ "\n"
+ " The normal of any vertex where :attr:`is_wire` is True will be a zero vector.\n");
+
static PyObject *bpy_bmesh_normal_update(BPy_BMesh *self)
{
BPY_BM_CHECK_OBJ(self);
@@ -1611,7 +1617,12 @@ static PyObject *bpy_bmvert_calc_shell_factor(BPy_BMVert *self)
PyDoc_STRVAR(bpy_bmvert_normal_update_doc,
".. method:: normal_update()\n"
"\n"
- " Update vertex normal.\n");
+ " Update vertex normal.\n"
+ " This does not update the normals of adjoining faces.\n"
+ "\n"
+ " .. note::\n"
+ "\n"
+ " The vertex normal will be a zero vector if vertex :attr:`is_wire` is True.\n");
static PyObject *bpy_bmvert_normal_update(BPy_BMVert *self)
{
BPY_BM_CHECK_OBJ(self);
@@ -1773,10 +1784,15 @@ static PyObject *bpy_bmedge_other_vert(BPy_BMEdge *self, BPy_BMVert *value)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpy_bmedge_normal_update_doc,
- ".. method:: normal_update()\n"
- "\n"
- " Update edges vertex normals.\n");
+PyDoc_STRVAR(
+ bpy_bmedge_normal_update_doc,
+ ".. method:: normal_update()\n"
+ "\n"
+ " Update normals of all connected faces and the edge verts.\n"
+ "\n"
+ " .. note::\n"
+ "\n"
+ " The normal of edge vertex will be a zero vector if vertex :attr:`is_wire` is True.\n");
static PyObject *bpy_bmedge_normal_update(BPy_BMEdge *self)
{
BPY_BM_CHECK_OBJ(self);
@@ -2012,7 +2028,8 @@ static PyObject *bpy_bmface_calc_center_bounds(BPy_BMFace *self)
PyDoc_STRVAR(bpy_bmface_normal_update_doc,
".. method:: normal_update()\n"
"\n"
- " Update face's normal.\n");
+ " Update face normal based on the positions of the face verts.\n"
+ " This does not update the normals of face verts.\n");
static PyObject *bpy_bmface_normal_update(BPy_BMFace *self)
{
BPY_BM_CHECK_OBJ(self);
diff --git a/source/blender/python/generic/blf_py_api.c b/source/blender/python/generic/blf_py_api.c
index 9e45105d105..060b7758ea9 100644
--- a/source/blender/python/generic/blf_py_api.c
+++ b/source/blender/python/generic/blf_py_api.c
@@ -50,7 +50,7 @@ static PyObject *py_blf_position(PyObject *UNUSED(self), PyObject *args)
PyDoc_STRVAR(py_blf_size_doc,
".. function:: size(fontid, size, dpi)\n"
"\n"
- " Set the size and dpi for drawing text.\n"
+ " Set the size and DPI for drawing text.\n"
"\n"
" :arg fontid: The id of the typeface as returned by :func:`blf.load`, for default "
"font use 0.\n"
diff --git a/source/blender/python/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/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c
index f93397eedab..dc9ad2ddb5e 100644
--- a/source/blender/render/intern/multires_bake.c
+++ b/source/blender/render/intern/multires_bake.c
@@ -464,140 +464,141 @@ static void do_multires_bake(MultiresBakeRender *bkr,
const MLoopTri *mlooptri = dm->getLoopTriArray(dm);
const int lvl = bkr->lvl;
int tot_tri = dm->getNumLoopTri(dm);
+ if (tot_tri < 1) {
+ return;
+ }
- if (tot_tri > 0) {
- MultiresBakeThread *handles;
- MultiresBakeQueue queue;
-
- MVert *mvert = dm->getVertArray(dm);
- MPoly *mpoly = dm->getPolyArray(dm);
- MLoop *mloop = dm->getLoopArray(dm);
- MLoopUV *mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV);
- float *pvtangent = NULL;
-
- ListBase threads;
- int i, tot_thread = bkr->threads > 0 ? bkr->threads : BLI_system_thread_count();
-
- void *bake_data = NULL;
-
- Mesh *temp_mesh = BKE_mesh_new_nomain(
- dm->getNumVerts(dm), dm->getNumEdges(dm), 0, dm->getNumLoops(dm), dm->getNumPolys(dm));
- memcpy(temp_mesh->mvert, dm->getVertArray(dm), temp_mesh->totvert * sizeof(*temp_mesh->mvert));
- memcpy(temp_mesh->medge, dm->getEdgeArray(dm), temp_mesh->totedge * sizeof(*temp_mesh->medge));
- memcpy(temp_mesh->mpoly, dm->getPolyArray(dm), temp_mesh->totpoly * sizeof(*temp_mesh->mpoly));
- memcpy(temp_mesh->mloop, dm->getLoopArray(dm), temp_mesh->totloop * sizeof(*temp_mesh->mloop));
- const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(temp_mesh);
- const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(temp_mesh);
-
- if (require_tangent) {
- if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) {
- BKE_mesh_calc_loop_tangent_ex(
- dm->getVertArray(dm),
- dm->getPolyArray(dm),
- dm->getNumPolys(dm),
- dm->getLoopArray(dm),
- dm->getLoopTriArray(dm),
- dm->getNumLoopTri(dm),
- &dm->loopData,
- true,
- NULL,
- 0,
- vert_normals,
- poly_normals,
- (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL),
- (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */
- /* result */
- &dm->loopData,
- dm->getNumLoops(dm),
- &dm->tangent_mask);
- }
-
- pvtangent = DM_get_loop_data_layer(dm, CD_TANGENT);
+ MultiresBakeThread *handles;
+ MultiresBakeQueue queue;
+
+ MVert *mvert = dm->getVertArray(dm);
+ MPoly *mpoly = dm->getPolyArray(dm);
+ MLoop *mloop = dm->getLoopArray(dm);
+ MLoopUV *mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV);
+ float *pvtangent = NULL;
+
+ ListBase threads;
+ int i, tot_thread = bkr->threads > 0 ? bkr->threads : BLI_system_thread_count();
+
+ void *bake_data = NULL;
+
+ Mesh *temp_mesh = BKE_mesh_new_nomain(
+ dm->getNumVerts(dm), dm->getNumEdges(dm), 0, dm->getNumLoops(dm), dm->getNumPolys(dm));
+ memcpy(temp_mesh->mvert, dm->getVertArray(dm), temp_mesh->totvert * sizeof(*temp_mesh->mvert));
+ memcpy(temp_mesh->medge, dm->getEdgeArray(dm), temp_mesh->totedge * sizeof(*temp_mesh->medge));
+ memcpy(temp_mesh->mpoly, dm->getPolyArray(dm), temp_mesh->totpoly * sizeof(*temp_mesh->mpoly));
+ memcpy(temp_mesh->mloop, dm->getLoopArray(dm), temp_mesh->totloop * sizeof(*temp_mesh->mloop));
+ const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(temp_mesh);
+ const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(temp_mesh);
+
+ if (require_tangent) {
+ if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) {
+ BKE_mesh_calc_loop_tangent_ex(
+ dm->getVertArray(dm),
+ dm->getPolyArray(dm),
+ dm->getNumPolys(dm),
+ dm->getLoopArray(dm),
+ dm->getLoopTriArray(dm),
+ dm->getNumLoopTri(dm),
+ &dm->loopData,
+ true,
+ NULL,
+ 0,
+ vert_normals,
+ poly_normals,
+ (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL),
+ (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */
+ /* result */
+ &dm->loopData,
+ dm->getNumLoops(dm),
+ &dm->tangent_mask);
}
- /* all threads shares the same custom bake data */
- if (initBakeData) {
- bake_data = initBakeData(bkr, ibuf);
- }
+ pvtangent = DM_get_loop_data_layer(dm, CD_TANGENT);
+ }
- if (tot_thread > 1) {
- BLI_threadpool_init(&threads, do_multires_bake_thread, tot_thread);
- }
+ /* all threads shares the same custom bake data */
+ if (initBakeData) {
+ bake_data = initBakeData(bkr, ibuf);
+ }
- handles = MEM_callocN(tot_thread * sizeof(MultiresBakeThread), "do_multires_bake handles");
-
- init_ccgdm_arrays(bkr->hires_dm);
-
- /* faces queue */
- queue.cur_tri = 0;
- queue.tot_tri = tot_tri;
- BLI_spin_init(&queue.spin);
-
- /* fill in threads handles */
- for (i = 0; i < tot_thread; i++) {
- MultiresBakeThread *handle = &handles[i];
-
- handle->bkr = bkr;
- handle->image = ima;
- handle->queue = &queue;
-
- handle->data.mpoly = mpoly;
- handle->data.mvert = mvert;
- handle->data.vert_normals = vert_normals;
- handle->data.mloopuv = mloopuv;
- BKE_image_get_tile_uv(ima, tile->tile_number, handle->data.uv_offset);
- handle->data.mlooptri = mlooptri;
- handle->data.mloop = mloop;
- handle->data.pvtangent = pvtangent;
- handle->data.precomputed_normals = poly_normals; /* don't strictly need this */
- handle->data.w = ibuf->x;
- handle->data.h = ibuf->y;
- handle->data.lores_dm = dm;
- handle->data.hires_dm = bkr->hires_dm;
- handle->data.lvl = lvl;
- handle->data.pass_data = passKnownData;
- handle->data.thread_data = handle;
- handle->data.bake_data = bake_data;
- handle->data.ibuf = ibuf;
-
- handle->height_min = FLT_MAX;
- handle->height_max = -FLT_MAX;
-
- init_bake_rast(&handle->bake_rast, ibuf, &handle->data, flush_pixel, bkr->do_update);
-
- if (tot_thread > 1) {
- BLI_threadpool_insert(&threads, handle);
- }
- }
+ if (tot_thread > 1) {
+ BLI_threadpool_init(&threads, do_multires_bake_thread, tot_thread);
+ }
+
+ handles = MEM_callocN(tot_thread * sizeof(MultiresBakeThread), "do_multires_bake handles");
+
+ init_ccgdm_arrays(bkr->hires_dm);
+
+ /* faces queue */
+ queue.cur_tri = 0;
+ queue.tot_tri = tot_tri;
+ BLI_spin_init(&queue.spin);
+
+ /* fill in threads handles */
+ for (i = 0; i < tot_thread; i++) {
+ MultiresBakeThread *handle = &handles[i];
+
+ handle->bkr = bkr;
+ handle->image = ima;
+ handle->queue = &queue;
+
+ handle->data.mpoly = mpoly;
+ handle->data.mvert = mvert;
+ handle->data.vert_normals = vert_normals;
+ handle->data.mloopuv = mloopuv;
+ BKE_image_get_tile_uv(ima, tile->tile_number, handle->data.uv_offset);
+ handle->data.mlooptri = mlooptri;
+ handle->data.mloop = mloop;
+ handle->data.pvtangent = pvtangent;
+ handle->data.precomputed_normals = poly_normals; /* don't strictly need this */
+ handle->data.w = ibuf->x;
+ handle->data.h = ibuf->y;
+ handle->data.lores_dm = dm;
+ handle->data.hires_dm = bkr->hires_dm;
+ handle->data.lvl = lvl;
+ handle->data.pass_data = passKnownData;
+ handle->data.thread_data = handle;
+ handle->data.bake_data = bake_data;
+ handle->data.ibuf = ibuf;
+
+ handle->height_min = FLT_MAX;
+ handle->height_max = -FLT_MAX;
+
+ init_bake_rast(&handle->bake_rast, ibuf, &handle->data, flush_pixel, bkr->do_update);
- /* run threads */
if (tot_thread > 1) {
- BLI_threadpool_end(&threads);
+ BLI_threadpool_insert(&threads, handle);
}
- else {
- do_multires_bake_thread(&handles[0]);
- }
-
- /* construct bake result */
- result->height_min = handles[0].height_min;
- result->height_max = handles[0].height_max;
+ }
- for (i = 1; i < tot_thread; i++) {
- result->height_min = min_ff(result->height_min, handles[i].height_min);
- result->height_max = max_ff(result->height_max, handles[i].height_max);
- }
+ /* run threads */
+ if (tot_thread > 1) {
+ BLI_threadpool_end(&threads);
+ }
+ else {
+ do_multires_bake_thread(&handles[0]);
+ }
- BLI_spin_end(&queue.spin);
+ /* construct bake result */
+ result->height_min = handles[0].height_min;
+ result->height_max = handles[0].height_max;
- /* finalize baking */
- if (freeBakeData) {
- freeBakeData(bake_data);
- }
+ for (i = 1; i < tot_thread; i++) {
+ result->height_min = min_ff(result->height_min, handles[i].height_min);
+ result->height_max = max_ff(result->height_max, handles[i].height_max);
+ }
- MEM_freeN(handles);
+ BLI_spin_end(&queue.spin);
- BKE_id_free(NULL, temp_mesh);
+ /* finalize baking */
+ if (freeBakeData) {
+ freeBakeData(bake_data);
}
+
+ MEM_freeN(handles);
+
+ BKE_id_free(NULL, temp_mesh);
}
/* mode = 0: interpolate normals,
diff --git a/source/blender/windowmanager/WM_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 1c3f7ed3e7a..5776184aec0 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -101,6 +101,7 @@ static int wm_operator_call_internal(bContext *C,
static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot);
static wmEvent *wm_event_add_mousemove_to_head(wmWindow *win);
+static void wm_operator_free_for_fileselect(wmOperator *file_operator);
/* -------------------------------------------------------------------- */
/** \name Event Management
@@ -1904,8 +1905,15 @@ void wm_event_free_handler(wmEventHandler *handler)
MEM_freeN(handler);
}
-/* Only set context when area/region is part of screen. */
-static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const wmEvent *event)
+/**
+ * Check if the handler's area and/or region are actually part of the screen, and return them if
+ * so.
+ */
+static void wm_handler_op_context_get_if_valid(bContext *C,
+ wmEventHandler_Op *handler,
+ const wmEvent *event,
+ ScrArea **r_area,
+ ARegion **r_region)
{
wmWindow *win = handler->context.win ? handler->context.win : CTX_wm_window(C);
/* It's probably fine to always use #WM_window_get_active_screen() to get the screen. But this
@@ -1913,12 +1921,15 @@ static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const
* possible. */
bScreen *screen = handler->context.win ? WM_window_get_active_screen(win) : CTX_wm_screen(C);
+ *r_area = NULL;
+ *r_region = NULL;
+
if (screen == NULL || handler->op == NULL) {
return;
}
if (handler->context.area == NULL) {
- CTX_wm_area_set(C, NULL);
+ /* Pass */
}
else {
ScrArea *area = NULL;
@@ -1942,7 +1953,7 @@ static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const
else {
ARegion *region;
wmOperator *op = handler->op ? (handler->op->opm ? handler->op->opm : handler->op) : NULL;
- CTX_wm_area_set(C, area);
+ *r_area = area;
if (op && (op->flag & OP_IS_MODAL_CURSOR_REGION)) {
region = BKE_area_find_region_xy(area, handler->context.region_type, event->xy);
@@ -1965,12 +1976,21 @@ static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const
/* No warning print here, after full-area and back regions are remade. */
if (region) {
- CTX_wm_region_set(C, region);
+ *r_region = region;
}
}
}
}
+static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const wmEvent *event)
+{
+ ScrArea *area = NULL;
+ ARegion *region = NULL;
+ wm_handler_op_context_get_if_valid(C, handler, event, &area, &region);
+ CTX_wm_area_set(C, area);
+ CTX_wm_region_set(C, region);
+}
+
void WM_event_remove_handlers(bContext *C, ListBase *handlers)
{
wmWindowManager *wm = CTX_wm_manager(C);
@@ -2017,7 +2037,13 @@ void WM_event_remove_handlers(bContext *C, ListBase *handlers)
}
WM_cursor_grab_disable(win, NULL);
- WM_operator_free(handler->op);
+
+ if (handler->is_fileselect) {
+ wm_operator_free_for_fileselect(handler->op);
+ }
+ else {
+ WM_operator_free(handler->op);
+ }
}
}
else if (handler_base->type == WM_HANDLER_TYPE_UI) {
@@ -2454,6 +2480,22 @@ static int wm_handler_operator_call(bContext *C,
return WM_HANDLER_BREAK;
}
+static void wm_operator_free_for_fileselect(wmOperator *file_operator)
+{
+ LISTBASE_FOREACH (bScreen *, screen, &G_MAIN->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ if (area->spacetype == SPACE_FILE) {
+ SpaceFile *sfile = area->spacedata.first;
+ if (sfile->op == file_operator) {
+ sfile->op = NULL;
+ }
+ }
+ }
+ }
+
+ WM_operator_free(file_operator);
+}
+
/**
* File-select handlers are only in the window queue,
* so it's safe to switch screens or area types.
@@ -2507,6 +2549,9 @@ static int wm_handler_fileselect_do(bContext *C,
case EVT_FILESELECT_CANCEL:
case EVT_FILESELECT_EXTERNAL_CANCEL: {
wmWindow *ctx_win = CTX_wm_window(C);
+ wmEvent *eventstate = ctx_win->eventstate;
+ /* The root window of the operation as determined in #WM_event_add_fileselect(). */
+ wmWindow *root_win = handler->context.win;
/* Remove link now, for load file case before removing. */
BLI_remlink(handlers, handler);
@@ -2542,21 +2587,17 @@ static int wm_handler_fileselect_do(bContext *C,
ED_fileselect_params_to_userdef(file_area->spacedata.first, win_size, is_maximized);
if (BLI_listbase_is_single(&file_area->spacedata)) {
- BLI_assert(ctx_win != win);
+ BLI_assert(root_win != win);
wm_window_close(C, wm, win);
- CTX_wm_window_set(C, ctx_win); /* #wm_window_close() NULLs. */
+ CTX_wm_window_set(C, root_win); /* #wm_window_close() NULLs. */
/* Some operators expect a drawable context (for #EVT_FILESELECT_EXEC). */
- wm_window_make_drawable(wm, ctx_win);
+ wm_window_make_drawable(wm, root_win);
/* Ensure correct cursor position, otherwise, popups may close immediately after
* opening (#UI_BLOCK_MOVEMOUSE_QUIT). */
- wm_cursor_position_get(
- ctx_win, &ctx_win->eventstate->xy[0], &ctx_win->eventstate->xy[1]);
- wm->winactive = ctx_win; /* Reports use this... */
- if (handler->context.win == win) {
- handler->context.win = NULL;
- }
+ wm_cursor_position_get(root_win, &eventstate->xy[0], &eventstate->xy[1]);
+ wm->winactive = root_win; /* Reports use this... */
}
else if (file_area->full) {
ED_screen_full_prevspace(C, file_area);
@@ -2575,7 +2616,13 @@ static int wm_handler_fileselect_do(bContext *C,
}
}
- wm_handler_op_context(C, handler, ctx_win->eventstate);
+ CTX_wm_window_set(C, root_win);
+ wm_handler_op_context(C, handler, eventstate);
+ /* At this point context is supposed to match the root context determined by
+ * #WM_event_add_fileselect(). */
+ BLI_assert(!CTX_wm_area(C) || (CTX_wm_area(C) == handler->context.area));
+ BLI_assert(!CTX_wm_region(C) || (CTX_wm_region(C) == handler->context.region));
+
ScrArea *handler_area = CTX_wm_area(C);
/* Make sure new context area is ready, the operator callback may operate on it. */
if (handler_area) {
@@ -2644,7 +2691,7 @@ static int wm_handler_fileselect_do(bContext *C,
}
if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
- WM_operator_free(handler->op);
+ wm_operator_free_for_fileselect(handler->op);
}
}
else {
@@ -2659,8 +2706,7 @@ static int wm_handler_fileselect_do(bContext *C,
wm->op_undo_depth--;
}
}
-
- WM_operator_free(handler->op);
+ wm_operator_free_for_fileselect(handler->op);
}
CTX_wm_area_set(C, NULL);
@@ -3981,58 +4027,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. */
@@ -4041,6 +4151,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);
+ }
}
/** \} */
@@ -5727,6 +5841,7 @@ void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win)
wmEvent test_event = *win->eventstate;
test_event.type = event_data[data_index].event_type;
test_event.val = event_data[data_index].event_value;
+ test_event.flag = 0;
wm_eventemulation(&test_event, true);
wmKeyMapItem *kmi = NULL;
for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) {
diff --git a/source/blender/windowmanager/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) {